- DelphiTools - https://www.delphitools.info -

Casting an Interface to a Class, the efficient way

Delphi 2010 added support for the “as” to cast an interface reference to its implementation class.

Here is an alternative that can help both performance and correctness.

Cast interface as class

type
   IFoo = interface ... end;
   TFoo = class (TInterfacedObject, IFoo) ... end;
...
var intf : IFoo;
var foo : TFoo;
...
intf := TFoo.Create;
...
foo := intf as TFoo; // get back the implementation class

However, if “as” can be convenient in certain scenarios, it’s alas not implemented very efficiently: the compiler and RTL go through several hoops to perform it (cf. this article [1] by Arnaud Bouchez). One of those hoops f.i. gets slower the more interfaces are implemented by the underlying class.

For instance in this benchmark [2], the “as” loop takes 5.9 ms when operating on a class implementing 2 interfaces, and 7.1 ms (20% more) when operating on a class implementing 8 interfaces (benchmark code adapted from this one [3] in the comments by Chris Rolliston [4])
Not visible in the benchmark is also the poor cache efficiency of that scanning, should you be dealing with an interface that is implemented by many different classes.

Potion of Speed-Casting

A faster way to go at it (about 4 to 6 times faster, even when not under stress), which is incidentally compatible with older Delphi versions, is to use something like

type
   IGetSelf = interface
      function GetSelf : TObject;
   end;
   IFoo = interface (IGetSelf)
      ...
   end;
...
procedure TFoo.GetSelf : TObject;
begin
   Result := Self;   
end;
...
foo := intf.GetSelf as TFoo;

and parent your Delphi interfaces to some base interface that provides a GetSelf or similar method, and implement it in a root class (in DWScript f.i., it is introduced by a TInterfacedSelfObject).

With the above code, a similar loop completes in 1.23 ms, constant-time, and doesn’t increase when classes implement many interfaces or if you have many classes implementing the same interface. So unlike “as“, it won’t fail you the more you stress it (I first bumped on the issue in a pathological case for “as” when multi-threading, where it ended up on top of profiling results for no good reason).

The limitation is that intf.GetSelf will fail if intf is nil (while “as” would just return a nil), though IME, when you’re casting back to the implementation, you’re likely to have filtered against nil far earlier in the code.

Another option would be Arnaud Bouchez’s ObjectFromInterface [1], which is constant-time and faster than “as”, but slightly slower than using an IGetSelf (about 7%), and you would be dealing with internal structures magic.

Beyond performance

A last benefit of the IGetSelf approach, beyond any performance considerations, is that it makes the cast part of the design.

Casting interfaces to classes is relevant only for Delphi-implemented classes and Delphi-oriented interfaces, going through an IGetSelf focuses the purposes and scope of those interfaces that are susceptible to be cast back to to classes, while “as” is more of a death-match trip-mine weapon, since you can invoke it on any interface.

Let’s not forget that casting an interface back to a class isn’t exactly a benign implementation choice: interfaces are often intended to isolate the implementation from the interfaces, if that isolation can be broken, that has to be a conscious design choice imho, more than an implementor’s shortcut.

As a bonus, using GetSelf allows to easily find where those cast are made in the code: mark the GetSelf method as deprecated in the IGetSelf, and the compiler will give you a complete lists of places where it’s used. So IGetSelf usage is easily diagnosable, and thus easily refactor-able. Try doing that with “as“…