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 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, 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 in the comments by Chris Rolliston)
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, 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“…

6 thoughts on “Casting an Interface to a Class, the efficient way

  1. I would (rather pedantically) point out that the terminology used here is loose. You aren’t casting to a *class*, your are casting to an *object*. I know Delphi confuses matters with the old style object, but if we don’t use the terminology class and object, how are we to differentiate between the type and the instance?

  2. @David Heffernan
    indeed…

    Or perhaps, I should say, casting to a *class instance* or *object instance* (this was the title of my linked article).

    That is, a *class* is the type information of an object, and a *class instance* is one particular object allocated in memory.

  3. @David Heffernan
    Well, arguably “instance” is less ambiguous than “object”, as demonstrated by your last sentence 😉

    But when casting, I would say you cast to a class and obtain an object, the distinction is subtile but it’s there: if intf is implemented by a TFoo, then “intf as TFoo” and “intf as TObject” will both return the same object, though they cast to different classes… If you say you cast to an object, you lose the important information that you’re not just casting to any object (of any class), but to an instance of a particular class (or one of its subclasses): you “cast to TFoo” or you “cast to TObject”, ie. specializations of the generic form “cast to <a class>” 😎

    I guess neither “cast to an object” nor “cast to a class” are pedantically correct, the first is semantically correct but vague and ambiguous, and the second is a semantic shortcut but it’s unambiguous (as long as meta-classes aren’t allowed to implement interfaces anyway)

  4. I don’t see that my final sentence demonstrates anything. Class and object are just two specialisations of type and instance. I take your point about `as TFoo` and `as TObject`. In standard OOP terminology class and object are very widely used and I just wish we could see past that awful TP object thing!

  5. I agree that casting an interface back to a class should not be done in “regular code”. But as Arnaud mentioned in his article it has its place in low level code. And there you cannot assume that every used interface “inherits” from the IGetSelf interface. So either stick with the as or some implementations that already worked in old Delphi versions.

  6. @Stefan Glienke
    Depends on what your low-level usage aims for, if it’s for diagnostics, crash reports or debugging, then yes, “as” working on any class can be interesting (but then you’ll likely just use “as TObject”).

    Otherwise, it’s a low-level cast that is part of a framework’s design, and then, there is no point in not making that part of the design.

    F.i. in DWScript the interfaces are used for memory management as wel as simplifying and stabilizing the interface, while the underlying objects expose a lot more functionality, some with safeties off, and the types, methods and properties of those objects are susceptible of being heavily refactored at any time.
    The interfaces are meant to be stable, if you code against them, then when upgrading changes should be minimal.

Comments are closed.