【问题标题】:Delphi interface generic function - Is there a work around?Delphi 接口通用函数 - 有解决方法吗?
【发布时间】:2013-05-08 03:22:56
【问题描述】:

我有以下代码

IProxy<T> = interface
  ['{1E3A98C5-78BA-4D65-A4BA-B6992B8B4783}']
  function Setup : ISetup<T>;
  function Proxy : T;
  function CastAs<I : IInterface> : IInterface;
end;

有没有办法解决编译时收到的编译器错误?

“[DCC 错误] Delphi.Mocks.pas(123): E2535 接口方法不能有参数化方法”

基本上,我希望通过传入要转换为的类型并返回该类型来传递此接口并能够将其丢弃。我可以通过一个类来实现这一点,但是更喜欢传递一个接口。

附加信息:

假设我有以下课程

TInterfaceProxy<T> = class(TBaseProxy<T>)
private type
  TProxyVirtualInterface = class(TVirtualInterface)
  private
    FProxy : TInterfaceProxy<T>;
  protected
  public
    function QueryInterface(const IID: TGUID; out Obj): HRESULT; override; stdcall;
    constructor Create(AProxy : TInterfaceProxy<T>; AInterface: Pointer; InvokeEvent: TVirtualInterfaceInvokeEvent);
  end;
private
  FVirtualInterfaces : TDictionary<TGUID, TProxyVirtualInterface>;
protected
  function InternalQueryInterface(const IID: TGUID; out Obj): HRESULT; stdcall;
  function QueryInterface(const IID: TGUID; out Obj): HRESULT; override;
  function Proxy : T;override;
  function CastAs<I: IInterface> : I;
public
  constructor Create;override;
  destructor Destroy;override;
end;

CastAs 在这里工作得很好,因为新请求的演员可以创建一个新的虚拟接口。现在,如果我想通过这门课就可以了。但是,如果我需要它作为接口,即TInterfaceProxy&lt;T&gt; = class(TBaseProxy&lt;T&gt;, IProxy&lt;T&gt;),它不起作用,请理解这一点。不同意,但理解。

因此,我如何绕过这个限制,以便我可以调用 CastAs 函数、传入一个类型(以任何接口类型开头),并能够从中创建虚拟接口?

【问题讨论】:

    标签: delphi generics interface


    【解决方案1】:

    正如编译器所说,接口不支持泛型参数化方法。

    没有解决方法,因为这是一个基本限制。类中的参数化方法是通过向类中的每个实例添加一个方法来实现的。这适用于类,因为它们是具体的,但不适用于接口。这是因为接口是一个函数表,并且该表的大小不能根据代码中其他地方出现的泛型方法实例而变化。出于类似的原因,泛型方法不能是虚拟的或动态的。

    您问题中的代码也有点误导。你写道:

    function CastAs<I : IInterface> : IInterface;
    

    但我确定你的意思是:

    function CastAs<I : IInterface> : I;
    

    无论如何,这是不可能的。一种选择是改用一个类。我同意这是一个绑定。

    如果你想在界面中做,你能做的最好的就是:

    function CastAs(const IID: TGUID): IInterface;
    

    但你必须这样称呼它:

    MyIntf := ProxyIntf.CastAs(IMyIntf) as IMyIntf;
    

    感觉有点恶心。

    选择你的毒药!

    【讨论】:

    • 谢谢。相信我将不得不通过 TGUID 并接受它。也会将类暴露在外部,但保留接口“function CastAs(const IID: TGUID): IInterface;”内部的。希望我能“用你的语言编程,而不是用它”programmers.stackexchange.com/questions/2777/…
    • 另外,您对function CastAs&lt;I : IInterface&gt; : I; 的正确看法是我复制代码时的错误。
    【解决方案2】:

    正如错误消息所述,接口中的方法不能具有通用参数。编译器根本不支持它,这是这样记录的:

    http://docwiki.embarcadero.com/RADStudio/XE4/en/Overview_of_Generics

    接口中的参数化方法

    不能在接口中声明参数化方法(使用类型参数声明的方法)。

    换句话说,您的CastAs 方法是非法的,因为它是在接口类型中声明的。另一方面,您一开始就不需要这种方法。您可以使用SysUtils.Supports() 代替将一个接口转换为另一个接口。

    【讨论】:

    • 明白,不是直接的问题。了解编译器在说什么,以及为什么它对 Delphi 来说是个问题。不同意这个设计,但是寻找一个为什么要绕过 Delphi 的限制来做我的设计。目前,我认为传递对象是唯一的方法。感谢 cmets。
    【解决方案3】:

    使用 Delphi 11.0 为我编译了以下类似的内容(尚未查看它是否崩溃):

    TStoryItemList = TList<IStoryItem>;
    
    TObjectListEx<T: class> = class
        class function GetAllOfInterface<AInterface: IInterface>(const list: TList<T>): TList<AInterface>;
      end;
    
    //...
    
    class function TObjectListEx<T>.GetAllOfInterface<AInterface>(const list: TList<T>): TList<AInterface>;
    var
      itemAsAInterface: AInterface;
    begin
      var guid := TRttiInterfaceType(TRttiContext.Create.GetType(TypeInfo(AInterface))).GUID;
      var listOfAInterface := TList<AInterface>.Create;
      for var item in list do
        if Supports(item, guid, itemAsAInterface) then
          listOfAInterface.Add(itemAsAInterface);
      result := listOfAInterface;
    end;
    

    用法

    function TStoryItem.GetStoryItems: TStoryItemList;
    begin
      result := TObjectListEx<TControl>.GetAllOfInterface<IStoryItem>(Controls);
    end;
    

    【讨论】:

    • 这是我的基于 Delphi 泛型的单元 Zoomicon.Generics.Collections 的旧摘录(现在接受 TPredicate 和 TPredicate 可选过滤器变得更强大),目前在存储库中我正在开发的应用程序:github.com/Zoomicon/READCOM_App(将来可能会移至自己的存储库)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-16
    • 1970-01-01
    • 2014-05-20
    • 1970-01-01
    • 1970-01-01
    • 2013-03-06
    相关资源
    最近更新 更多