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