【问题标题】:Cast a TInterfacedObject to an interface将 TInterfacedObject 强制转换为接口
【发布时间】:2011-07-23 19:27:54
【问题描述】:

According to the Delphi docs,我可以使用as 运算符将TInterfacedObject 转换为接口。

但这对我不起作用。强制转换给出编译错误:“运算符不适用于此操作数类型”。

我使用的是 Delphi 2007。

这是一些代码(控制台应用程序)。包含错误的行被标记。

program Project6;

{$APPTYPE CONSOLE}

uses
  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.

【问题讨论】:

  • docwiki 是否正确?链接页面中的第一句话是“实现接口的类可以使用 as 运算符对接口进行动态绑定。”。

标签: delphi interface delphi-2007


【解决方案1】:

快速解答

您的界面需要有一个 GUID,as 运算符才能工作。转到IMyInterface = interface 之后的第一行,在任何方法定义之前,然后按 Ctrl+G 以生成新的 GUID。

更长的评论

接口的as 运算符需要一个GUID,因为它调用IUnknown.QueryInterface,而这又需要一个GUID。如果您在将 INTERFACE 转换为其他类型的 INTERFACE 时遇到此问题,那也没关系。

首先,您不应该将TInterfacedObject 强制转换为接口,因为这意味着您同时持有对实现对象的引用 (TInterfacedObject) 和对已实现接口的引用 (@ 987654327@)。这是有问题的,因为您混合了两个生命周期管理概念:TObject 一直到有人调用 .Free 为止;您有理由确定在您不知情的情况下,没有任何东西会在您的对象上调用.Free。但是接口是引用计数的:当您将接口分配给变量时,引用计数器会增加,当该实例超出范围(或分配其他内容)时,引用计数器会减少。当引用计数器达到零时,对象将被释放 (.Free)!

这里有一些看起来很无辜的代码,很快就会遇到很多麻烦:

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;

修复非常简单:将O 的类型从TMyObject[...] 更改为IMyInterface,如下所示:

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.

【讨论】:

  • @David,感谢您纠正我的错别字。对错别字本身感到抱歉,我经常依赖 Opera 的拼写检查器,但仍然有一些错误可以通过 :( 尤其是(对我而言)同音异义词,因为 Opera 不会将它们标记为拼写错误,老实说不要注意到他们。再次感谢。
【解决方案2】:

如果要使用 As 或 Supports 运算符,则需要在接口中添加 Guid,例如:

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

docwiki

【讨论】:

  • 哦,是的。复制粘贴代码示例的乐趣!谷歌你提供的 GUID,显然很多人认为复制粘贴代码是个好主意!
  • 是的,它是复制的,它是从我链接到的 docwiki 复制的。我看不出有什么问题,因为 OP 无论如何都应该生成一个唯一的 GUID...
  • 实际上,@Cosmin,从我得到的 Google 结果来看,似乎每个人都复制了整个 Delphi 语言指南,而不仅仅是那个 GUID。
【解决方案3】:

如果您将对象 o 定义为正确的类型,则转换将是自动的。否则,您可以随时使用supports() 和/或自己致电QueryInterface

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

【讨论】:

  • Supports()QueryInterface() 都需要 GUID。如果as 运算符不起作用,那么这两个变体也不起作用。
  • QCosmin:是的,你是对的。我总是用 GUID 定义我的接口,所以我没有注意到...
  • 您仍然持有这两个参考文献,请阅读 @David 的 answer 来回答同样的问题。
猜你喜欢
  • 2014-01-16
  • 2013-02-02
  • 2012-08-05
  • 1970-01-01
  • 1970-01-01
  • 2015-04-11
  • 2011-01-05
  • 2013-04-17
相关资源
最近更新 更多