比如我想写的代码是
procedure TForm11.FormCreate(Sender: TObject);
begin
Button1.OnClick := procedure(Sender: TObject)
begin
ShowMessage('Button1 Clicked');
end;
end;
这样直接编译不过去,因为一个是函数引用,是个是对象方法函数引用 reference to procedure(Sender: TObject)
对象方法 procedure(Sender: TObject) of object;
带着疑问拜google,得到下面的解决方法,连接地址
procedure TForm11.FormCreate(Sender: TObject);
begin
@Button1.OnClick := PPointer(Cardinal(PPointer(
procedure(Sender: TObject)
begin
ShowMessage('Button1 Clicked');
end
)^) + $0C)^;
end;
很神奇,原理是说 匿名函数被编译器编译成了一个接口类,$0C的地址所指,也就是我写代码测试了几次,能得出一个结论,在一个函数内定义的函数引用类型变量,会编译成“函数名$ActRec”的类(姑且叫ActRec),并只产生一个ActRec的实例,每个函数引用变量就对应着一个接口,有几个函数变量,就会有几个接口,然后这个ActRec都会实现这几个接口。
但是如果匿名函数太长,单独定义写呢
procedure TForm11.FormCreate(Sender: TObject);
type
TNotifyProc = reference to procedure(Sender: TObject);
TNotifyXXX = reference to procedure(a: Integer; b: Integer);
var
B1,B2: TNotifyPro;
Bi: Interface;
Bo: TObject;
xxx: TNotifyXXX;
begin
B1 := procedure(Sender: TObject)
begin
ShowMessage('Button1 Clicked');
end;
B2 := procedure(Sender: TObject)
begin
ShowMessage('Button1 Clicked');
end;
xxx := procedure(a: Integer; b: Integer)
begin
ShowMessage('xxx');
end;
Bi := PUnknown(@B1)^; //函数引用其实是个接口来的
Bo := Bi as TObject; //将接口转为为实体类
showmessage(Bo.ClassName); //TForm11.FormCreate$ActRec,该类实现了3个接口
@Button1.OnClick := PPointer(Cardinal(PPointer(
Integer(Bi) //可行
)^) + $0C)^;
@Button1.OnClick := PPointer(Cardinal(PPointer(
PInteger(@B1)^ //可行
)^) + $0C)^;
@Button1.OnClick := PPointer(Cardinal(PPointer(
Bo.GetInterfaceTable^.Entries[0].VTable //这样不行,为啥????
)^) + $0C)^;end;
难道接口变量已经不是指向VTable所指了???? 不过这种靠编译器的,毕竟不是好方法,继续找找看。
没有评论:
发表评论