如何在Delphi 10.2中使用接口方法作为方法类型的参数

时间:2018-09-03 17:38:57

标签: delphi

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

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

  

[dcc32错误] Unit1.pas(66):E2010不兼容的类型:“ TMyMethodRef”和“过程,无类型指针或无类型参数”

最小示例:

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 )

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

我知道有一个解决方案可以实现另一个接口来访问对象引用,但是它并不安全(我无法始终确保类同时实现两个接口),并且我不希望这种解决方案:< / p>

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;

2 个答案:

答案 0 :(得分:7)

你不能那样做。

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

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;

答案 1 :(得分:1)

如果您不想维护适配器类并管理它们的寿命,并且可以随意更改为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。另外,通常,并且在您已经在使用接口的情况下,如果可能,请切换为始终使用接口。