"InheritsFrom"和"is"运算符之间有什么区别?

9
在我所记得的所有情况中,以下指令都会产生相同的结果:
type
  TMyClass = class(TObject);
  TMyChildClass = class(TMyClass);

var
  MyObj : TMyChildClass;

procedure TForm1.Test();
var
  ResultA : Boolean;
  ResultB : Boolean;
begin
  //Using TObject.InheritsFrom
  ResultA := MyObj.InheritsFrom(TMyClass);

  //Using 'is' operator
  ResultB := MyObj is TMyClass;

  //Showing results
  ShowMessage(
    'InheritsFrom = ' + BoolToStr(ResultA, True) + sLineBreak +
    'is = ' + BoolToStr(ResultB, True)
  );
end;

使用is运算符与TObject.InheritsFrom函数有何区别?

3个回答

10
is运算符是基于InheritsFrom构建的。因此,
obj is TSomeClass

被实现为

(obj <> nil) and obj.InheritsFrom(TSomeClass)

表达式obj.InheritsFrom(TSomeClass)可能有点令人困惑,因为它看起来像是一个实例方法。实际上,InheritsFrom是一个类方法,obj的运行时类被传递给InheritsFrom作为Self指针。
因此,从根本上讲,isInheritsFrom执行相同的任务,至少在限制关注于类时是如此。请注意,is更通用,并且也可以与接口一起使用。
有明显的语法差异。即is需要一个实例,而InheritsFrom是一个类函数。尽管如我们所见,Delphi语言确实支持在实例引用上调用类函数。另一个明显的区别是,is具有对nil引用的内置测试。
这些只是语法上的差异,基本操作是相同的,这可以通过is调用InheritsFrom来证明。

9

是的,它们之间有区别。InheritsFrom 是一个类函数,其主要目的是测试类是否继承自某个类。

你不能在类上使用 is 操作符。

TMyChildClass is TMyClass 会编译失败,但你可以使用 TMyChildClass.InheritsFrom(TMyClass) 替代。


尽管它们之间存在一些细微差别,但我们不应忽略它们具有相似之处的事实。 - Alec
在这个意义上,我是指在某些情况下它们可以实现相同的结果! - Alec

3

我想至少提及一个案例

obj is TSomeClass

运算符的行为并不完全一样

(obj <> nil) and obj.InheritsFrom(TSomeClass)

当编译器“认为”obj只能是TSomeClass成员时,它将跳过TObject.InheritsFrom()调用(使用is运算符时),并仅测试空指针。这可以通过以下测试代码确认(在10.2.3东京和10.3.3里约测试通过):

procedure TestForTStringList(obj: TStringList);
begin
  if obj.InheritsFrom(TStringList) then
  begin
    //
  end;

  if obj is TStringList then
  begin
    //
  end;
end;

这段代码会生成以下的汇编代码

enter image description here

在这个方法中,我们可以看到obj.InheritsFrom()实际上调用了TObject.InheritsFrom()方法。另一方面,与is运算符相关的代码仅检查空指针。
与编译器为无法在编译时确定类的is运算符生成的代码进行比较:

enter image description here

以上,变量被声明为TObject,所以编译器实际上调用了System.IsClass(),其代码如下:
function _IsClass(const Child: TObject; Parent: TClass): Boolean;
begin
  Result := (Child <> nil) and Child.InheritsFrom(Parent);
end;

你可能认为这没有任何区别,但在某些特定的调试场景下确实有所不同:当使用FastMM并选中检查释放对象上的方法调用选项时。在这种情况下,FastMM会用TFreedObject实例替换实际对象。
在这种情况下,当obj实际上是一个TFreedObject实例(而不是TStringList)时。
obj.InheritsFrom(TStringList) = False

另一方面

(obj is TStringList) = True

因为代码实际上只会检查空指针。

PS:生成的代码与编译器优化设置无关。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接