如何欺騙 Delphi 將一般獨立 procedure 當成 TxxEvent of Class; |
|
Justmade
版主 發表:94 回覆:1934 積分:2030 註冊:2003-03-12 發送簡訊給我 |
很簡單的幾行程式碼,其實是騙分不是騙 Delphi 這是早前硑究 class="code">
unit Unit1; interface uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls; type
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end; var
Form1: TForm1; implementation {$R *.dfm} function ProcToMethod(Proc : Pointer; Obj : TObject) : TMethod;
begin
result.Code := Proc;
result.Data := Obj;
end; procedure MyClick(Self : TObject;Sender : TObject);
begin
ShowMessage(TComponent(Self).Name '.' TComponent(Sender).Name);
end; procedure FormClick(Self : TObject ; Sender : TObject);
begin
ShowMessage('Hey! don''t click me !');
end; procedure TForm1.FormCreate(Sender: TObject);
begin
onClick := TNotifyEvent(ProcToMethod(@FormClick,Self));
Button1.OnClick := TNotifyEvent(ProcToMethod(@MyClick,Self));
end; end.
謝謝 Timhuang 兄的提醒我之前傳上的版本是我試成後測試失敗的版本有Bug
現在上面的已是更正及加強了的,測試過可用。 發表人 - Justmade 於 2003/05/16 21:57:33
|
timhuang
尊榮會員 發表:78 回覆:1815 積分:1608 註冊:2002-07-15 發送簡訊給我 |
|
Justmade
版主 發表:94 回覆:1934 積分:2030 註冊:2003-03-12 發送簡訊給我 |
|
sryang
尊榮會員 發表:39 回覆:762 積分:920 註冊:2002-06-27 發送簡訊給我 |
引言: 呵呵,我做好後想試有沒有更好(不需使用MyMethod)的方法,但沒試成功,以為自己沒 Save file 所以直接上傳並 Copy 到這裡,完全沒再看看真失敗。 現在我會更新它,寫個函數處理 ProcToMethod ,這樣以後就可以將一般 Procedure 一句 Assign 給 Evnet 了。 發表人 - Justmade 於 2003/05/16 22:12:41不用 TMethod 做轉型是行不通的,請看 On-line Help 中 "Procedural types" 說明中的一段話: 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. 加油喔,喵~
------
歡迎參訪 "腦殘賤貓的備忘錄" http://maolaoda.blogspot.com/ |
Justmade
版主 發表:94 回覆:1934 積分:2030 註冊:2003-03-12 發送簡訊給我 |
[補貼電子報 006 期之解釋文章以便以後查閱]
【文章】如何欺騙 Delphi 將一般獨立 procedure 指給 Evnet
【作者】Justmade
【內文】Procedure, Method, Evnet 的架構小探討 前言 這是早前研究 RTTI 時的副產品,受 timhuang 兄及 william 兄的啟發不少。
獨立 Procedure / Function 與 Method 的異同獨立的 Procedure / Function 是差不多有 Programming 便開始有了,就是可執行的一個程式區段。 Method 其實也是 Procedure / Function , 只是它們是屬於一個 Object 的,可自由的存取 Object 內的資源,但它們不能獨立於 Object 外,即是要該 Object 的物件被 Create 了才可使用。*註一 到底為甚麼 Method 可以存取 Object 的物件呢? (重點) 原來,所有 Method 都有一個 隱藏的第一參數,就是 Self ,而且當 Method 存取非 Local 的資料時,會先預設到 Self 去找,找不到才到 Globel 去找。 另外 Method 的定義比一般 Procedure / Function 多了一個資料就是 Self 啦,這個資料就是當Method 被呼叫時自動變成第一個參數的值。TMethod 的定義如下 : TMethod = record Code : Pointer; // 執行碼(等同一般 Procedure) 的 Pointer Data : Pointer; // Self 的 Pointer end; EventEvent 其實是個 Property,而這個 Property 記著當某種事件發生事雖要執行的 Procedure 。 由於 procedure 可以有無數不同的參數規格,所以每個 Event 都會指定指定該 Event 所需的 Property 規格,其中最常用的,可算是 TNotifyEvent 了。 TNotifyEvnet 的規格是 : type TNotifyEvent = procedure (Sender: TObject) of object; 即是說要附合 TNotifyEvent, 就要符合兩個條件 1. 參數一個,一定要是 TObject 形態 (名稱 Sender 可修改) 2. of Object 的意思是一定要是屬於 Object 的 (亦即是 Method 啦)如何欺騙 Delphi 將一般獨立 procedure 指給 Evnet縱合以上的知識,我們可以得知 procedure 比 Method 欠了兩樣東西 : 1. 沒有了隱藏的第一參數 Self 2. 不是 TMethod 格式, 少了 Data 部份 所以要一般獨立 procedure 變成 Method 就要從這兩方便下手 : 1. Self 參數 由於呼叫 Method 時是會自動傳 Self 作第一個參數,其他的參數順移一個的,Method 亦懂得自動在最前加入接受 Self 這個參數。但獨立 Procedure 沒這個機制,我們只好手動幫它定義 Self 這個參數了,例如我們想定義一個附合 TNotifyEvent 的獨立 procedure,便要定義成 : procedure MyClick(Self : TObject,Sender : TObject);*註二 2. 轉成 TMethod 格式 我們只要建立一個 TMethod, 將 Procedure 的 pointer 給 TMethod.Code 再將一個 Object 給 TMethod.Data 就可以了 我寫了一個公用函數來將 Procedure 轉做 Method :function ProcToMethod(Proc : Pointer; Obj : TObject) : TMethod; begin result.Code := Proc; result.Data := Obj; end;比方說若要將 Procedure MyClick 轉做 Form1 的 Method 並指給 Button1 的 onClick 事件,只要使用 : Button1.onClick := TNotifyEvnet(ProcToMethod(@MyClick,Form1)); 由於 ProcToMethod 所回傳的 Method 是動態產生的,Complier 不知道這是否合乎 TNotifyEvnet 所以要人手指定它是 TNotifyEvent。 好處與壞處好處 : 1. 不用將與 Form 沒甚麼關後的 Event 全放在 TFormX 的 Type 裡 2. 也不用另建 Class 甚至不用在 interface 部份宣告 3. 可以將常用的與 Object 沒甚麼直接關係的 Event 集中到某些 PAS file 分類共享 4. 可以將 procedure export, 那便可以放在 DLL 裡給主程式呼叫 (Method 不能 export 但 獨立 procedure 可以) 壞處 : 1. 不能在設計畫面直接指給物件的事件 2. 若 Self 定太寬(如 TObject) 很多 Self 的property/method 不能存取 (TypeCast 不如定在 Self 參數) * 註三 3. 若 Self 定大緊(如 TForm1) 則共用性變小,而且若放在分開 PAS implementation 部份要反 uses Form1 的 unit 取得 Form1 的 type範例說明unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; procedure FormCreate(Sender: TObject); private test : string; public { Public declarations } end; var Form1: TForm1; // 可不用宣告 MyClick / FormClick implementation {$R *.dfm} function ProcToMethod(Proc : Pointer; Obj : TObject) : TMethod; // 將Procedure 轉化 Method 格式 begin result.Code := Proc; // 執行碼(等同一般 Procedure) 的 Pointer result.Data := Obj; // Self 的 Pointer end; procedure MyClick(Self : TObject;Sender : TObject); // 作為 TNotifyEvent 的獨立 procedure, Self 只定作 TObject begin ShowMessage(TComponent(Self).Name '.' TComponent(Sender).Name); end; procedure FormClick(Self : TForm1 ; Sender : TObject); // 作為 TNotifyEvent 的獨立 procedure, Self 定作 TForm1 begin ShowMessage(Self.test); // 可使用 Form1 的變數 end; procedure TForm1.FormCreate(Sender: TObject); begin onClick := TNotifyEvent(ProcToMethod(@FormClick,Self)); // 設定 FormClick 做 Form 的 OnClick Event Button1.OnClick := TNotifyEvent(ProcToMethod(@MyClick,Self)); // 要 Typecast Method 為 TNotifyEvent test := 'This is a private variable!'; // 設定變數 end; end. 結論由於這對小弟來說是較新及較深的東西,所以希望各前輩高手多給意見及指出錯誤,若早有前人這樣做亦可列出參考資料給大學習。若大家有興趣,可在範例主題或另開新題討論,探討這個技術的可使用性和使這技術更成熟。 -------------------------------------------------------------------- 註一 : ClassMethod 除外, ClassMethod 可在 Class 未被 Create 時使用,但卻也有限制不能使用 Class 內一般的Method / Property 因可能 Class 未被 Create -- 這方便謝謝 TimHuang 兄的指教 註二 : Self 不一定要是 TObject, 若一定是 Form用的可以 是 TForm, 甚至 TForm1 也可以 註三 : 若配合 RTTI 的使用 String 便可存取物件 property 的方法,應可加大這種做法的可共用性,例如 Form1, Form3 Form5 都有 Test 這個變數,而某 Proc 的 Self 只定義成 TForm 而沒有 Test 這個變數,這個 Proc 仍可使用 RTTI 來存取 Test 這個變數以使此 Proc 可同時支援供 Form1, Form3, Form5 共用。 |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |