将对象转换为接口类型,但没有TInterfacedObject作为基类

9
我需要一个实现接口且不使用引用计数的类。我按照以下方式进行操作:
  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;

缺少引用计数是可以正常工作的。但我担心的是,我无法使用as运算符将TMyClass强制转换为IMyInterface:
var
  MyI: IMyInterface; 
begin
  MyI := TMyClass.Create as IMyInterface;

我遇到了以下问题:

[DCC Error] E2015运算符不适用于此操作数类型

TMyClassTInterfacedObject 派生时,该问题会消失——也就是说,我可以进行这种转换而没有编译器错误。显然,我不想使用 TInterfacedObject 作为基类,因为这将使我的类引用计数。为什么这样的转换被禁止,如何解决呢?


通过在接口声明中添加 GUID,可能会获得更好的结果。在“= interface”行之后添加一行新行,并按下 Ctrl-Shft-G。例如 asGetInterfacesupports 等需要能够通过 GUID 识别接口才能正常工作。 - Marjan Venema
你没有仔细阅读我的帖子。当我从TInterfacedObject派生时,它可以工作。GUID在这里没有任何作用。你只需要在使用COM时才需要GUID。 - Michał Szczygieł
1
不,你不仅需要GUID来处理COM。在许多方面,你需要GUID来处理接口。你尝试过添加GUID并查看发生了什么吗? - Marjan Venema
添加GUID后还是一样的问题。我会编辑我的代码。 - Michał Szczygieł
1
@DavidHeffernan 在将 IInterface 添加到列表后,它可以工作。您能否发布一个答案,以便我可以接受它并附带一些解释? - Michał Szczygieł
显示剩余3条评论
1个回答

15
您无法在代码中使用as的原因是,您的类没有在其支持的接口列表中显式列出IInterface。即使您的接口派生自IInterface,除非您实际上列出该接口,否则您的类不支持它。

因此,解决方法很简单,只需像这样声明您的类:

TMyClass = class(TObject, IInterface, IMyInterface)

你的类需要实现 IInterface 接口,因为编译器依赖于此来实现 as 强制类型转换。
另外,我想说的是,通常应该避免使用接口继承。大多数情况下,它没有什么用处。使用接口的好处之一是,你不受实现继承所带来的单一继承约束的限制。
但无论如何,所有的 Delphi 接口 都自动继承自 IInterface,所以在你的情况下没有必要指定。我会像这样声明你的接口:
IMyInterface = interface
  ['{B84904DF-9E8A-46E0-98E4-498BF03C2819}'] 
  procedure InterfaceMethod;
end;

更广泛地说,你应该努力避免在接口中使用继承。采用这种方法将促进较少的耦合,从而导致更大的灵活性。


接口继承不是实现继承。在Delphi中,任何接口都派生自IInterface。最后一句话不清楚。 - kludg
@Serg,接口继承不是实现继承,这正是我试图表达的观点。我会尝试澄清一下。 - David Heffernan
值得一提的是,使用接口继承可以减小实例大小。无论您使用 TMyClass = class(TObject, IMyInterface) 还是 TMyClass = class(TObject, IInterface, IMyInterface),在 Delphi XE 中实例大小都为 12 字节,因为 IMyInterface 继承自 IInterface。显式声明 IInterface 支持仅在编译时很重要,并且不会使代码变得臃肿。 - kludg
@David,你最可能想要写的是“避免使用接口继承”,而不是“避免使用实现继承”。我认为这个“笔误”让Serg感到困惑了。 - iamjoosy
就我的今日经验而言,我忘记在我的接口中添加GUID,并且使用AS操作符时编译器报错了。我完全忘记了这一点,并对事实视而不见。 - Eduardo Elias
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接