将 TInterfacedObject 转换为接口

发布于 2024-10-27 03:44:18 字数 902 浏览 4 评论 0原文

根据Delphi文档,我可以转换TInterfacedObject 使用 as 运算符连接到接口。

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

我正在使用 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.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

若言繁花未落 2024-11-03 03:44:18

快速回答

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

更长的注释

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

首先,您不应该将 TInterfacedObject 转换为接口,因为这意味着您同时持有对实现对象 (TInterfacedObject) 的引用和对已实现接口 (IMyInterface) 的引用。这是有问题的,因为您混合了两个生命周期管理概念: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[...]O 的类型> 到 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.

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.
江挽川 2024-11-03 03:44:18

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

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

请参阅 docwiki

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

笑脸一如从前 2024-11-03 03:44:18

如果您将对象 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.

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.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文