Cast a TInterfacedObject to an interface
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.
精彩评论