Delphi 中的 procedure of object


其實要了解這些東西,適當的學些反匯編,WINDOWS內存管理機制,PE結構,看下李維的VCL架構剖析可以很好理解
type
  TMyEvent = procedure of object;
這是一種數據類型的定義,他定義了一個可以在類中使用的函數類型
區別於
type
  TMyProc = procedure;

TMyEvent 和 TMyProc 都定義了一個函數類型,他們的差別是,TMyProc 不可以用在類中定義事件,TMyEvent 卻可以。

如果你想知道問什么,那就需要深入了解事件類型以及函數類型到底是個什么?

procedure a();
begin
  ShowMessage()
end;

var
  Func1: TMyProc;
  Func2: TMyEvent;

  func1 := A;

func1 := A;

這句到底發生了什么事情呢?

在32位的windows操作系統中,任何一個進程被執行,操作系統要完成的工作基本上包括:為這個進程開辟一個線性的虛擬地址表(從$00000000到$FFFFFFFF),然后把exe程序加載到這個地址表對應的內存中,然后跳入到exe程序的入口點,剩下的事情,就是exe自己從入口點開始一步一步干自己的事情了。
程序中,所有的函數都被編譯到固定的位置,也就是每個函數都有一個地址與之對應,對函數的調用,實際上就是在寄存器或者堆棧中准備好參數,然后跳入到函數對應的地址繼續執行。
func1 := A;
這個動作,完成的就是把函數A的地址賦值給func1這個變量。


那么,為Func2: TMyEvent 賦值又是完成了什么呢?

類中的函數也是有地址的,編譯后,類中的函數也會被編譯到一個固定的地址上。
但是,func2不是表示一個函數地址,他是一個記錄類型的變量
這個可以通過 SizeOf(TMyEvent) 看到 SizeOf(TMyEvent) 返回值是8,而SizeOf(TMyProc)返回的是4
實際上,TMyEvent類型是包含兩個域的一個記錄,其中一個域是Code,也就是和TMyProc一樣的,另一個域是Data,他保存着一個對象。

  Button2.OnClick := Button1Click;
這樣的賦值,實際上是將窗體類的對象form1給data域,將Button1Click這個函數的地址給Code域
 
 
 
 
 
 
 
1.
TMyEvent = procedure of object; TMyEvent 是方法類型 就等於 String 是一種數據類型

如在一個Txxx類中定義了一個過程 
procedure Txxx.test;
begin
  showmessage('1');
end;

那么就可以把 Txxx.test 賦給 TMyEvent 了 (TMyEvent := Txxx.test);
因為 TMyEvent 的定義中有 of object 所以賦值給它的只能是類中的過程,而不能是普通過程。

2.
FOnHundred: TMyEvent; --> FOnHundred 是一個變量 它的類型是 TMyEvent, 
  就等於 icount : integer 這么簡單

3.
property OnHundred: TMyEvent read FOnHundred write FOnHundred

OnHundred 是一個屬性 它的類型也是 TMyEvent 通過 FOnHundred 變量來存取 這個屬性的值。

當屬性不用過程去存取的時候 (如上例)調用 OnHundred 和 FOnHundred 是相同的
 

delphi中經常見到以下兩種定義

Type

TMouseProc = procedure (X,Y:integer);

TMouseEvent = procedure (X,Y:integer) of Object;

兩者樣子差不多但實際意義卻不一樣,

TMouseProc只是單一的函數指針類型;

TMouseEvent是對象的函數指針,也就是對象/類的函數/方法

區別在於類方法存在一個隱藏參數self,也就是說兩者形參不一樣,所以不能相互轉換。

這也就是為什么delphi中可以這樣賦值 button1.onClick:=button2.onClick;

卻不能這樣賦值 button1.onclick=buttonclick; (buttonclick為本地函數,button2.onclick為類方法)的原因!


方法類型定義:TMethod = procedure of object;

 

Procedural types allow you to treat procedures and functions as values that can be assigned to variables or passed to other procedures and functions. For example, suppose you define a function called Calc that takes two integer parameters and returns an integer:

function Calc(X,Y: Integer): Integer;

You can assign the Calc function to the variable F:

 

var F: function(X,Y: Integer): Integer;

F := Calc;

If you take any procedure or function heading and remove the identifier after the word procedure or function, what’s left is the name of a procedural type. You can use such type names directly in variable declarations (as in the example above) or to declare new types:

Type

TIntegerFunction = function: Integer;

TProcedure = procedure;

TStrProc = procedure(const S: string);

TMathFunc = function(X: Double): Double;

Var

F: TIntegerFunction;{ F is a parameterless function that returns an integer }

Proc: TProcedure;   { Proc is a parameterless procedure }

SP: TStrProc;       { SP is a procedure that takes a string parameter }

M: TMathFunc;       { M is a function that takes a Double (real) parameterand returns a Double }

procedure FuncProc(P: TIntegerFunction);  { FuncProc is a procedure whose only parameter is a parameterless integer-valued function }

The variables above are all procedure pointers—that is, pointers to the address of a procedure or function. If you want to reference a method of an instance object (see Classes and objects), you need to add the words of object to the procedural type name. For example

Type

TMethod = procedure of object;

TNotifyEvent = procedure(Sender: TObject) of object;

These types represent method pointers. A method pointer is really a pair of pointers; the first stores the address of a method, and the second stores a reference to the object the method belongs to. Given the declarations

Type

TNotifyEvent = procedure(Sender: TObject) of object;

TMainForm = class(TForm)

procedure ButtonClick(Sender: TObject);

...

end;

var

MainForm: TMainForm;

OnClick: TNotifyEvent

we could make the following assignment.OnClick := MainForm.ButtonClick;

Two procedural types are compatible if they have the same calling convention,the same return value (or no return value), and the same number of parameters, with identically typed parameters in corresponding positions. (Parameter names do not matter.)

Procedure pointer types are always incompatible with method pointer types. The value nil can be assigned to any procedural type.

Nested procedures and functions (routines declared within other routines) cannot be used as procedural values, nor can predefined procedures and functions. If you want to use a predefined routine like Length as a procedural value, write a wrapper for it:

function FLength(S: string): Integer;

begin

Result := Length(S);

end;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM