Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the differences between InheritsFrom and the is operator?

Tags:

delphi

In all cases I can remember, the following instructions give the same result:

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 there some difference in using the is operator instead of TObject.InheritsFrom function?

like image 435
Fabrizio Avatar asked Feb 10 '17 08:02

Fabrizio


3 Answers

Yes, there is a difference. InheritsFrom is class function and it's primary purpose is testing whether class IS (inherits from some class).

You cannot use is operator on classes.

TMyChildClass is TMyClass would not compile, but you can use TMyChildClass.InheritsFrom(TMyClass) instead.

like image 178
Dalija Prasnikar Avatar answered Nov 12 '22 15:11

Dalija Prasnikar


The is operator is built on top of InheritsFrom. So,

obj is TSomeClass

is implemented as

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

The expression obj.InheritsFrom(TSomeClass) is perhaps a little confusing because it looks like InheritsFrom is an instance method. In fact InheritsFrom is a class method, and the runtime class of obj is passed to InheritsFrom as the Self pointer.

So fundamentally is and InheritsFrom perform the same task, at least when restricting attention to classes. Note that is is more general and can also be used with interfaces, for example.

There are obvious syntactical differences. Namely that is requires an instance, whereas InheritsFrom is a class function. Although, as we have seen, the Delphi language does support calling class functions on instance references. And the other obvious difference is that is has a built-in test for a nil reference.

These are just syntactical differences though, the fundamental operation is the same, as evidenced by the fact that is calls InheritsFrom.

like image 40
David Heffernan Avatar answered Nov 12 '22 15:11

David Heffernan


I'd like to mention at least one case where

obj is TSomeClass

operator doesn't behave exactly as

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

When the compiler "thinks" that obj can only be a TSomeClass member, it will skip the TObject.InheritsFrom() call (when using the is operator) and only test for a nil pointer. This can be confirmed by the following test code (tested in 10.2.3 Tokyo and 10.3.3 Rio):

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

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

This code produces the following assembly code

enter image description here

In this method, we can see that obj.InheritsFrom() actually calls TObject.InheritsFrom() method. On the other hand, the code related to the is operator only checks for a nil pointer.

Compare with the code generated by the compiler for an is operator when the class can't be determined at compile time:

enter image description here

Above, the variable was declared as TObject, so the compiler actually calls System.IsClass(), which is:

function _IsClass(const Child: TObject; Parent: TClass): Boolean;
begin
  Result := (Child <> nil) and Child.InheritsFrom(Parent);
end;

You may think that this doesn't make any difference, but it does in some specific debugging scenarios: When using FastMM and the option to check for method calls on a freed object is checked. In this case, FastMM replaces the actual object with a TFreedObject instance.

In this case when obj is actually a TFreedObject instance (not a TStringList).

obj.InheritsFrom(TStringList) = False

On the other hand

(obj is TStringList) = True

because the code will actually only check for a nil pointer.

PS: the generated code is the same regardless of compiler optimization settings

like image 2
Alexandre M Avatar answered Nov 12 '22 13:11

Alexandre M