【问题标题】:How can I use an interface method as a parameter of method type in Delphi 10.2如何在 Delphi 10.2 中使用接口方法作为方法类型的参数
【发布时间】:2018-09-03 17:38:57
【问题描述】:

我有一个类实例和两个对它的引用。有一个对象引用 (aMII : TMyInterfaceImpl) 和一个接口引用 (iMI : IMyInterface)。 当我使用对象引用将其方法之一分配给方法类型变量 (methodRef : TMyMethodRef) 时,它工作正常。

但是如果我想在同样的情况下使用接口引用,编译器会抛出错误:

[dcc32 Error] Unit1.pas(66): E2010 Incompatible types: 'TMyMethodRef' and 'procedure, untyped pointer or untyped parameter'

最小的例子:

type
  TMyMethodRef = procedure of object;
  //TMyMethodRef = procedure of interface; // Invalid definition, just a desperate attempt

  IMyInterface = interface
    ['{BEA60A2B-C20F-4E02-A938-65DD4332ADB0}']
    procedure foo;
  end;

  TMyInterfaceImpl = class ( TInterfacedObject, IMyInterface )
    procedure foo;
  end;

procedure TMyInterfaceImpl.foo;
begin
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  aMII : TMyInterfaceImpl;
  iMI : IMyInterface;
  methodRef : TMyMethodRef;
begin
  aMII := TMyInterfaceImpl.Create;
  iMI := aMII;
  methodRef := aMII.foo; // It compiles
  methodRef := iMI.foo; // It does not compile

end;

在我的原始来源中,我必须使用接口(我没有对象引用,因为抽象工厂设计模式:function TMyFactory.createMyInterface : IMyInterface)。

而且我必须使用它的一种方法作为某些事件的回调函数(作为参数传递给 setter 方法:procedure TMyInterfaceImpl2.setOnEvent( onEvent_ : TMyMethodRef )

我该怎么做?如何将接口引用的方法之一分配给方法类型变量?

我知道有一种解决方案可以实现另一个接口来访问对象引用,但它并不安全(我无法确定该类是否始终实现这两个接口)并且我不想要这个解决方案:

type
  TMyMethodRef = procedure of object;

  IMyInterface = interface
    ['{BEA60A2B-C20F-4E02-A938-65DD4332ADB0}']
    procedure foo;
  end;

  TMyInterfaceImpl = class;

  IInstanceAccessor<T> = interface
    ['{61E36FF2-A9D3-4B46-BAE8-217CBE7A1F18}']
    function getInstance : T;
  end;

  TMyInterfaceImpl = class ( TInterfacedObject, IMyInterface, IInstanceAccessor<TMyInterfaceImpl> )
    procedure foo;
    function getInstance : TMyInterfaceImpl;
  end;

procedure TMyInterfaceImpl.foo;
begin
end;

function TMyInterfaceImpl.getInstance : TMyInterfaceImpl;
begin
  result := self;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  iMI : IMyInterface;
  aMII : TMyInterfaceImpl;
  methodRef : TMyMethodRef;
begin
  iMI := TMyInterfaceImpl.Create;
  aMII := ( iMI as IInstanceAccessor<TMyInterfaceImpl> ).getInstance;
  methodRef := aMII.foo;
end;

【问题讨论】:

  • 创建一个包含对接口的引用的记录类型。在该记录类型上实现一个调用接口方法的方法。如果您愿意,也可以使用类而不是记录。
  • @DavidHeffernan David 如果您创建答案,我会接受。 Uwe 解决方案也可以正常工作,但在我看来它是一个更好的解决方案。记录类型分配/销毁是自动完成的,不像对象实例那样。
  • Uwe 的回答很好。这和我的一样。有时记录会更好。有时上课更好。一切都取决于。例如,如果您希望包装器比声明它的函数更持久,那么记录就不是那么好。概念才是最重要的。
  • @DavidHeffernan 在我原来的类似环境中尝试后,我意识到我必须存储记录/适配器实例......我认为如果我在接口之间建立依赖关系并通过有更好的解决方案调用接口本身作为参数。 :( 或者我从一个祖先接口派生它,它只包含回调方法并将该方法作为参数传递。

标签: delphi


【解决方案1】:

你不能这样做。

你可以做的是用这样的方法声明一个适配器类,并使用该方法作为回调。

type
  TMyInterfaceAdapter = class
  private
    FIntf: IMyInterface;
  public
    procedure Foo;
    property Intf: IMyInterface read FIntf write FIntf;
  end;

procedure TMyInterface.Foo;
begin
  Assert(Assigned(FIntf), 'no interface assigned');
  FIntf.Foo;
end;

【讨论】:

  • 这是一个可行的解决方案。感谢您的时间和精力。我接受并投了赞成票。但在我看来,David 发布的那个(还只是评论)似乎更好。记录分配/销毁会自动进行。
【解决方案2】:

如果您不想维护适配器类和管理它们的生命周期,并且可以随意更改为TMyMethodRef = System.SysUtils.TProc,您也可以使用匿名方法:

var
  Impl: TMyInterfaceImpl;
  Intf: IMyInterface;
  MethodRef: TMyMethodRef;
begin
  Impl := TMyInterfaceImpl.Create;
  MethodRef := Impl.Foo;
  MethodRef;
  Intf := Impl; // Be sure to capture it!
  MethodRef := procedure begin Intf.foo; end;
  MethodRef;
end;

这在后台工作,有点类似于what you described in your own comment。此外,一般而言,由于您已经在使用接口,因此如果可能,请切换到始终使用接口。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多