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?
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.
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.
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

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:

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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With