全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:2858
推到 Plurk!
推到 Facebook!

如何欺騙 Delphi 將一般獨立 procedure 當成 TxxEvent of Class;

 
Justmade
版主


發表:94
回覆:1934
積分:2030
註冊:2003-03-12

發送簡訊給我
#1 引用回覆 回覆 發表時間:2003-05-16 16:56:37 IP:218.16.xxx.xxx 未訂閱
很簡單的幾行程式碼,其實是騙分不是騙 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
附加檔案:30573_ProcToMethod.zip
timhuang
尊榮會員


發表:78
回覆:1815
積分:1608
註冊:2002-07-15

發送簡訊給我
#2 引用回覆 回覆 發表時間:2003-05-16 21:30:00 IP:61.221.xxx.xxx 未訂閱
發現小小 bug, 修一下就可以了.... Justmade 兄的這篇文章很有參考價值哦~    
procedure TForm1.FormCreate(Sender: TObject);
var
  MyMethod : TMethod;
begin
  MyMethod.Code := @MyClick;
  MyMethod.Data := Self;
  Button1.OnClick := TNotifyEvent(MyMethod);
end;
Justmade
版主


發表:94
回覆:1934
積分:2030
註冊:2003-03-12

發送簡訊給我
#3 引用回覆 回覆 發表時間:2003-05-16 21:53:54 IP:218.16.xxx.xxx 未訂閱
呵呵,我做好後想試有沒有更好(不需使用MyMethod)的方法,但沒試成功,以為自己沒 Save file 所以直接上傳並 Copy 到這裡,完全沒再看看真失敗。 現在我會更新它,寫個函數處理 ProcToMethod ,這樣以後就可以將一般 Procedure 一句 Assign 給 Evnet 了。 發表人 - Justmade 於 2003/05/16 22:12:41
sryang
尊榮會員


發表:39
回覆:762
積分:920
註冊:2002-06-27

發送簡訊給我
#4 引用回覆 回覆 發表時間:2003-05-19 09:22:46 IP:202.3.xxx.xxx 未訂閱
引言: 呵呵,我做好後想試有沒有更好(不需使用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

發送簡訊給我
#5 引用回覆 回覆 發表時間:2003-05-26 00:06:35 IP:61.10.xxx.xxx 未訂閱
[補貼電子報 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;

Event

Event 其實是個 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 共用。
系統時間:2024-04-26 12:25:45
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!