开发者

Cast a TInterfacedObject to an interface

开发者 https://www.devze.com 2023-02-19 20:35 出处:网络
According to the Delphi docs, I can cast a TInterfacedObject to an interface using the as operator. But it doesn\'t work for me. The cast gives a compile error: \"Operator not applicable to this oper

According to the Delphi docs, I can cast a TInterfacedObject to an interface using the as operator.

But it doesn't work for me. The cast gives a compile error: "Operator not applicable to this operand type".

I'm using Delphi 2007.

Here is some code (a console app). The line that contains the error is marked.

program Project6;

{$APPTYPE CONSOLE}

uses
开发者_C百科  SysUtils;

type
  IMyInterface = interface
    procedure Foo;
  end;

  TMyInterfacedObject = class(TInterfacedObject, IMyInterface)
  public
    procedure Foo;
  end;

procedure TMyInterfacedObject.Foo;
begin
  ;
end;

var
  o: TInterfacedObject;
  i: IMyInterface;
begin
  try
    o := TMyInterfacedObject.Create;
    i := o as IMyInterface;  // <--- [DCC Error] Project6.dpr(30): E2015 Operator not applicable to this operand type
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end.


Quick answer

Your interface needs to have a GUID for the as operator to work. Go to the first line after IMyInterface = interface, before any method definitions and hit Ctrl+G to generate a new GUID.

Longer comment

The as operator for interfaces requires a GUID because it calls IUnknown.QueryInterface, and that in turn requires a GUID. That's all right if you run into this problem when casting an INTERFACE to an other kind of INTERFACE.

You're not supposed to cast an TInterfacedObject to an interface in the first place, because that means you're holding both a reference to the implementing object (TInterfacedObject) and a reference to the implemented interface (IMyInterface). That's problematic because you're mixing two lifecycle management concepts: TObject live until something calls .Free on them; You're reasonably sure nothing calls .Free on your objects without your knowledge. But interfaces are reference-counted: when you assign your interface to a variable the reference counter increases, when that instance runs out of scope (or is assigned something else) the reference counter is decreases. When the reference counter hits ZERO the object is disposed of (.Free)!

Here's some innocent-looking code that's going to get into a real lot of trouble fast:

procedure DoSomething(If: IMyInterface);
begin
end;

procedure Test;
var O: TMyObjectImplementingTheInterface;
begin
  O := TMyObjectImplementingTheInterface.Create;
  DoSomething(O); // Works, becuase TMyObject[...] is implementing the given interface
  O.Free; // Will likely AV because O has been disposed of when returning from `DoSomething`!
end;

The fix is very simple: Change the O's type from TMyObject[...] to IMyInterface, like this:

procedure DoSomething(If: IMyInterface);
begin
end;

procedure Test;
var O: IMyInterface;
begin
  O := TMyObjectImplementingTheInterface.Create;
  DoSomething(O); // Works, becuase TMyObject[...] is implementing the given interface
end; // `O` gets freed here, no need to do it manually, because `O` runs out of scope, decreases the ref count and hits zero.


If you want to use the As or Supports operator you need to add a Guid to the Interface, example:

type   
  IMyInterface = interface
    ['{00000115-0000-0000-C000-000000000049}']
    procedure Foo;   
  end; 

See the docwiki


The cast will be automatic of you define the object o as the correct type. Otherwise, you can always use supports() and/or call QueryInterface yourself.

var
  o: TMyInterfacedObject;
  i: IMyInterface;
begin
  try
    o := TMyInterfacedObject.Create;
    i := o;
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end.
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号