如何在兼容的方法指针之间进行转换?

时间:2016-04-12 13:05:01

标签: delphi polymorphism function-pointers delphi-xe7

我有两个不同的方法指针。

type
  TComponentMethod = procedure(const AComponent: TComponent) of object;
  TFormMethod = procedure(const AForm: TForm) of object;

唯一的区别是参数的类型,但两者都是对象引用,所以它不应该与调用约定的观点有任何区别。

(但由于共同/逆转,它可能是一种类型安全问题。)

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    procedure M2(const AForm: TForm);
  end;

procedure TForm1.FormCreate(Sender: TObject);
var
  FormMethod: TFormMethod;
  ComponentMethod: TComponentMethod;
begin
  FormMethod := M2;
  // How to cast this?
  ComponentMethod := M2;
end;

编译器不允许我这样做。

[dcc32 Error] Unit1.pas(32): E2010 Incompatible types: 'TComponent' and 'TForm'

有没有办法输入方法指针到另一个"兼容"方法指针?

3 个答案:

答案 0 :(得分:4)

你可以这样做:

var
  FormMethod: TFormMethod;
  ComponentMethod: TComponentMethod;
begin
  FormMethod := M2;
  ComponentMethod := TComponentMethod(FormMethod);
end;

据我所知,诀窍是你需要先分配一个临时局部变量,然后再将其分配给ComponentMethod

如您所知,这不是类型安全的。如果使用不是从ComponentMethod派生的参数调用TForm,则编译器将无法保存您。

答案 1 :(得分:3)

他们不兼容。

如果M2TForm作为其输入之一,编译器可以合理地期望在M2的正文中访问表单方法/成员。

TComponentMethod事件处理程序只需要一个TComponent实例来调用它。因此,组合(如果编译器允许)可以访问TForm实例上的TComponent成员。 ( 显然是灾难的秘诀 。)

E.g。

procedure TForm1.M2(const AForm: TForm);
begin
  AForm.ModalResult := mrCancel;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  ComponentMethod: TComponentMethod;
begin
  ComponentMethod := M2;

  //The next line is legal. But if the previous line were legal,
  //you'd attempt to access TForm(AComponent).ModalResult ...
  //An AV if you're lucky, and weird behaviour if you're not.
  ComponentMethod(TComponent.Create(Self));
end;

那就是说,你可以进行艰难的转型。但是,TComponentMethod(M2)不起作用,因为编译器几乎在您使用标识符的任何时候都想调用M2。它只是一点“编译魔术”,它首先允许FormMethod := M2。因此,您需要一个中间事件处理程序变量来保存对M2的引用。并且因为事件处理程序变量不是函数,所以可以在不尝试调用它的情况下进行类型转换。

Temp := M2;
ComponentMethod := TComponentMethod(Temp);

警告 这是一个糟糕的主意,但它有效:

  • 提醒:ComponentMethod变量的使用方法如下:ComponentMethod(AComponentThatsNotAForm)
  • 如果您能真正保证以前的通话因为M2的实施方式而安全,那么您应该按如下方式声明M2:procedure M2(const AComponent: TComponent);(如果没有不需要一个表单实例来完成它的工作,不要求表单实例。)... 你不必费心去拼命寻找一种方法来射击自己。< / em>的

答案 2 :(得分:-1)

戴维斯的回答很好。我编写了一个通用方法,以避免每次演示时都必须声明源类型的临时变量。

type
  TOneArgMethod<T> = procedure(const A: T) of object;


function TForm1.CastMethod<TFrom, TTo>(AFrom: TOneArgMethod<TFrom>)
  : TOneArgMethod<TTo>;
var
  Method: TMethod;
begin
  Method := TMethod(AFrom);
  Result := TOneArgMethod<TTo>(Method);
end;