参考程序和自我
reference to procedure and self
当我在一个 TForm
(比如说,MyForm
)中有一个过程 oneProc
(procedure TMyForm.oneProc
)时,如果我在 oneProc
(procedure TMyForm.oneProc
)的任何过程中=12=] 类似于 TThread.Queue(nil, oneProc);
,当 oneProc()
被触发时 Self
被很好地初始化为 MyForm
是如何结束的?
你能解释一下为什么会这样吗?
procedure TMyForm.Button1Click(Sender: TObject);
begin
ProcA(ProcB);
end;
procedure TMyForm.ProcA(const Aproc: Tproc);
begin
Aproc;
end;
procedure TMyForm.ProcB;
begin
showmessage(self.className); // << this show TTMyForm (how?)
end;
首先,TThread.Queue()
被重载以接受 TThreadMethod
(非匿名)和 TThreadProcedure
(匿名)参数。您的文字描述适用于将调用非匿名版本的代码。另一方面,您的代码示例做的事情与您描述的 TThread.Queue()
代码所做的有些不同。但无论如何,回答你的问题-
一个非匿名方法指针实际上携带了2个值——一个是方法的地址,一个是传递给方法的Self
参数的值(方法指针由TMethod
记录).
TProc
是对匿名方法的引用,非匿名方法指针可以赋值给匿名方法引用。 documentation 甚至在“使用匿名方法”一节中这样说:
Method references can also be assigned to methods as well as anonymous methods. For example:
type
TMethRef = reference to procedure(x: Integer);
TMyClass = class
procedure Method(x: Integer);
end;
var
m: TMethRef;
i: TMyClass;
begin
// ...
m := i.Method; //assigning to method reference
end;
However, the converse is not true: you cannot assign an anonymous method to a regular method pointer. Method references are managed types, but method pointers are unmanaged types. Thus, for type-safety reasons, assigning method references to method pointers is not supported. For instance, events are method pointer-valued properties, so you cannot use an anonymous method for an event. See the section on variable binding for more information on this restriction.
在内部,匿名方法引用是作为编译器生成的引用计数接口实现的,具有单个 Invoke()
方法。当您在代码中编写匿名方法时,编译器会生成一个隐藏的 class,它使用该代码实现 Invoke()
。匿名方法捕获的任何变量都存储为 class.
的成员
将非匿名方法指针分配给匿名方法引用时,编译器会生成一个 class 来捕获该方法指针,然后在生成的 Invoke()
.[=29 中调用它=]
因此,根据您显示的代码,编译器会将其翻译成大致 如下所示(我省略了不相关的实现细节):
type
//TProc = reference to procedure;
TProc_Intf = interface
procedure Invoke;
end;
TProc_Generated = class(TInterfacedObject, TProc_Intf)
FProc: procedure of object; // type of TMyForm.ProcB()
procedure Invoke;
end;
procedure TProc_Generated.Invoke;
begin
FProc; // <-- calls FProc.Code with FProc.Data as Self!
end;
procedure TMyForm.Button1Click(Sender: TObject);
var
Intf: TProc_Intf;
begin
//ProcA(ProcB);
Intf := TProc_Generated.Create;
//TProc_Generated(Intf).FProc := @ProcB;
TMethod(TProc_Generated(Intf).FProc).Code := Addr(ProcB);
TMethod(TProc_Generated(Intf).FProc).Data := Self; // <-- Self stored here!
ProcA(Intf);
end;
procedure TMyForm.ProcA(const Aproc: {TProc}TProc_Intf);
begin
//Aproc;
Aproc.Invoke;
end;
procedure TMyForm.ProcB;
begin
ShowMessage(Self.ClassName); // <-- Self valid here!
end;
这就是 Self
从 Button1Click()
到 ProcB()
通过 ProcA()
的方式。
当我在一个 TForm
(比如说,MyForm
)中有一个过程 oneProc
(procedure TMyForm.oneProc
)时,如果我在 oneProc
(procedure TMyForm.oneProc
)的任何过程中=12=] 类似于 TThread.Queue(nil, oneProc);
,当 oneProc()
被触发时 Self
被很好地初始化为 MyForm
是如何结束的?
你能解释一下为什么会这样吗?
procedure TMyForm.Button1Click(Sender: TObject);
begin
ProcA(ProcB);
end;
procedure TMyForm.ProcA(const Aproc: Tproc);
begin
Aproc;
end;
procedure TMyForm.ProcB;
begin
showmessage(self.className); // << this show TTMyForm (how?)
end;
首先,TThread.Queue()
被重载以接受 TThreadMethod
(非匿名)和 TThreadProcedure
(匿名)参数。您的文字描述适用于将调用非匿名版本的代码。另一方面,您的代码示例做的事情与您描述的 TThread.Queue()
代码所做的有些不同。但无论如何,回答你的问题-
一个非匿名方法指针实际上携带了2个值——一个是方法的地址,一个是传递给方法的Self
参数的值(方法指针由TMethod
记录).
TProc
是对匿名方法的引用,非匿名方法指针可以赋值给匿名方法引用。 documentation 甚至在“使用匿名方法”一节中这样说:
Method references can also be assigned to methods as well as anonymous methods. For example:
type TMethRef = reference to procedure(x: Integer); TMyClass = class procedure Method(x: Integer); end; var m: TMethRef; i: TMyClass; begin // ... m := i.Method; //assigning to method reference end;
However, the converse is not true: you cannot assign an anonymous method to a regular method pointer. Method references are managed types, but method pointers are unmanaged types. Thus, for type-safety reasons, assigning method references to method pointers is not supported. For instance, events are method pointer-valued properties, so you cannot use an anonymous method for an event. See the section on variable binding for more information on this restriction.
在内部,匿名方法引用是作为编译器生成的引用计数接口实现的,具有单个 Invoke()
方法。当您在代码中编写匿名方法时,编译器会生成一个隐藏的 class,它使用该代码实现 Invoke()
。匿名方法捕获的任何变量都存储为 class.
将非匿名方法指针分配给匿名方法引用时,编译器会生成一个 class 来捕获该方法指针,然后在生成的 Invoke()
.[=29 中调用它=]
因此,根据您显示的代码,编译器会将其翻译成大致 如下所示(我省略了不相关的实现细节):
type
//TProc = reference to procedure;
TProc_Intf = interface
procedure Invoke;
end;
TProc_Generated = class(TInterfacedObject, TProc_Intf)
FProc: procedure of object; // type of TMyForm.ProcB()
procedure Invoke;
end;
procedure TProc_Generated.Invoke;
begin
FProc; // <-- calls FProc.Code with FProc.Data as Self!
end;
procedure TMyForm.Button1Click(Sender: TObject);
var
Intf: TProc_Intf;
begin
//ProcA(ProcB);
Intf := TProc_Generated.Create;
//TProc_Generated(Intf).FProc := @ProcB;
TMethod(TProc_Generated(Intf).FProc).Code := Addr(ProcB);
TMethod(TProc_Generated(Intf).FProc).Data := Self; // <-- Self stored here!
ProcA(Intf);
end;
procedure TMyForm.ProcA(const Aproc: {TProc}TProc_Intf);
begin
//Aproc;
Aproc.Invoke;
end;
procedure TMyForm.ProcB;
begin
ShowMessage(Self.ClassName); // <-- Self valid here!
end;
这就是 Self
从 Button1Click()
到 ProcB()
通过 ProcA()
的方式。