Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Casting object to interface type with no TInterfacedObject as base class

I need a class implementing interface with no reference counting. I did the following:

  IMyInterface = interface(IInterface)
      ['{B84904DF-9E8A-46E0-98E4-498BF03C2819}'] 
      procedure InterfaceMethod;
  end;

  TMyClass = class(TObject, IMyInterface)
  protected
      function _AddRef: Integer;stdcall;
      function _Release: Integer;stdcall;
      function QueryInterface(const IID: TGUID; out Obj): HResult;stdcall;
  public
      procedure InterfaceMethod;
  end;

  procedure TMyClass.InterfaceMethod;
  begin
      ShowMessage('The Method');
  end;

  function TMyClass.QueryInterface(const IID: TGUID; out Obj): HResult;
  begin
      if GetInterface(IID, Obj) then
          Result := 0
      else
          Result := E_NOINTERFACE;
  end;

  function TMyClass._AddRef: Integer;
  begin
      Result := -1;
  end;

  function TMyClass._Release: Integer;
  begin
      Result := -1;
  end;

Lack of reference counting works fine. But my concern is that I cannot cast TMyClass to IMyInterface using as operator:

var
  MyI: IMyInterface; 
begin
  MyI := TMyClass.Create as IMyInterface;

I am given

[DCC Error] E2015 Operator not applicable to this operand type

The problem disappears when TMyClass derives from TInterfacedObject - i.e. I can do such casting without compiler error. Obviously I do not want to use TInterfacedObject as a base class as it would make my class reference counted. Why is such casting disallowed and how one would workaround it?

like image 438
Michał Szczygieł Avatar asked Feb 18 '13 08:02

Michał Szczygieł


People also ask

Can you cast an interface to an object?

If you have a concrete class, you can cast it to the interface. If you have an interface, it is possible to cast to the concrete class. Generally, you only want to go in the first direction. The reason being that you shouldn't know what the concrete class is when you have only a pointer to the interface.

What is interface in Delphi?

In Delphi, "interface" has two distinct meanings. In OOP jargon, you can think of an interface as a class with no implementation. In Delphi unit definition interface section is used to declare any public sections of code that appear in a unit.

What is casting to an interface?

You can access the members of an interface through an object of any class that implements the interface. For example, because Document implements IStorable , you can access the IStorable methods and property through any Document instance: Document doc = new Document("Test Document"); doc.


1 Answers

The reason you cannot use as in your code is that your class does not explicitly list IInterface in its list of supported interfaces. Even though your interface derives from IInterface, unless you actually list that interface, your class does not support it.

So, the trivial fix is to declare your class like this:

TMyClass = class(TObject, IInterface, IMyInterface)

The reason that your class needs to implement IInterface is that is what the compiler is relying on in order to implement the as cast.

The other point I would like to make is that you should, in general, avoid using interface inheritance. By and large it serves little purpose. One of the benefits of using interfaces is that you are free from the single inheritance constraint that comes with implementation inheritance.

But in any case, all Delphi interfaces automatically inherit from IInterface so in your case there's no point specifying that. I would declare your interface like this:

IMyInterface = interface
  ['{B84904DF-9E8A-46E0-98E4-498BF03C2819}'] 
  procedure InterfaceMethod;
end;

More broadly you should endeavour not to use inheritance with your interfaces. By taking that approach you will encourage less coupling and that leads to greater flexibility.

like image 63
David Heffernan Avatar answered Oct 23 '22 20:10

David Heffernan