對.NET Framework "事件"機制理解的程式碼分析 (轉)
下面的文章是我自認為對"事件"機制理解比較透徹的後寫的程式碼分析,但寫完之後,鄙人又惟恐理解有所偏差,所以特貼出來讓各位高手指教一二,若能讓發現理解錯誤之處,將不勝感激(此問完全乃本人"獨立自主"之作,絕非抄襲)
同時我相信此文對初學者也有一定幫助!
為了闡述清晰,特舉例說明;
該範例是在一箇中完全自定義一組事件,並在另外的集中對事件被激發作出反映(也就是事件被激發後預先定義好的方法).
一.含有自定義事件的控制元件主體程式碼及對應剖析 (注意,此控制元件庫是由VS的"新建"->"控制元件庫"生成的)
namespace MyEventTEST
{
public class LoginEventArgs : System.EventArgs
// 上面程式碼定義了在主程式中引發事件時需要傳遞給主程式的所有資訊,並且注意,
// 該類必須派生於System.EventArgs類
{
public LoginEventArgs(string sUserID, string sPass, bool bValid)
{
UserID = sUserID;
Password = sPassword;
Valid = bValid;
}
public string UserID;
public string Password;
public bool Valid;
}
public delegate void GoodLoginEventHandler( sender, LoginEventArgs e);
public delegate void FailedThreeTimesEventHandler(object sender, LoginEventArgs e);
// 上面兩行程式碼定義了兩個多路委託(因此返回型別必須為void),每個委託對應一種型別的事件;
// 因為是多路委託,所以每個委託中可以含有多個方法.
// 請注意,引數是(object sender, LoginEventArgs e),所以新增到多路委託的方法必須符合這種簽名方式.
// 此外,為什麼這裡不使用已經定義的多路委託"System.EventHandler(object sender, EventArgs e)",
// 而要自己定義新的委託"?????EventHandler()"呢?這是因為我們這裡傳遞給程式集的引數不是
// "System.EventArgs"型別,而是自己定義的"LoginEventArgs"型別,所以有必要重新定義自己的委託型別.
public class ActiveLogin : System.Windows.Forms.UserControl
{
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.TextBox txtUserID;
private System.Windows.Forms.TextBox txtPass;
private System.Windows.Forms.Button btnLogin;
private System.Windows.Forms.Button btnCancel;
private System.ComponentModel.Container components = null;
// 上面程式碼是組成這個控制元件的一些定義,由VS.NET自動生成
public event GoodLoginEventHandler GoodLogin;
public event FailedThreeTimesEventHandler FailedThreeTimes;
public event EventHandler Cancel;
// 上面三行程式碼非常之重要,定義了三個事件(event),分別是"GoodLogin","FailedThreeTimes"
// 和"Cancel"
// 它們的型別分別是"GoodLoginEventHandler","FailedThreeTimesEventHandler"
// 和"EventHandler",也就是說新增到這三個事件中的方法必須符合對應的多路委託定義!
// 而且注意,因為事件"Cancel"的型別是系統已經定義的多路委託"EventHandler"型別,
// 所以上面的多路委託中沒有定義類似"CancelEventHandler"的新委託,因為是不需要的.
public ActiveLogin()
{
InitializeComponent();
}
// 上面程式碼是控制元件中類"ActiveLogin"的構造方法,該方法中呼叫了初始方法InitializeComponent()
// 上面程式碼由VS.NET自動生成
protected overr void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
// 上面程式碼是自定義控制元件中類"ActiveLogin"的析構方法,由VS.NET自動生成.
private void InitializeComponent()
{
.... // 這裡是對所有引用控制元件(元件)的初始化程式碼
}
// 上面程式碼是自定義控制元件中類"ActiveLogin"的初始方法,其中內容由VS.NET自動生成.
protected virtual void OnGoodLogin(LoginEventArgs e)
// 上面一行程式碼定義了激發"GoodLogin"事件的方法;
// 注意簽名型別,定義方法是protected virtual,也就是說只能在這個類及它的
// 繼承類中訪問此方法,而且可以重寫.
// 引數型別是"LoginEventArgs",注意只有這一個引數,因為在本方法中含有對
// this的引用,所以這裡不需要傳遞this.
// 一般地說,這個方法使用場合只有兩種:
// <1>在本控制元件內被呼叫,因為本方法不被呼叫,就無法激發使用者程式碼在事件"GoodLogin"
// 中新增的方法;
// <2>在使用者的繼承程式碼中重寫本方法,雖然重寫本方法可能會帶來的提高,但
// 倘若使用者程式碼中忘記呼叫此方法,那麼在使用者程式碼先前在事件"GoodLogin"中
// 新增的方法將無法得到啟用!!! (避免此問題的方法就是在重寫方法中必須含有
// 一行"base.GoogLogin(e)",這行將負責呼叫本方法)
// 對於第<2>點需要提出的是,在使用者的繼承程式碼中重寫本方法的作用相當與在
// 事件"GoodLogin"中新增一個方法,此方法的程式碼內容和重寫方法內容相同.
// (但是應該絕對沒有"base.GoogLogin(e)"這一行)
{
if (GoodLogin != null) // 如果在事件"GoogLogin"中含有方法,則激發這些方法
{
GoodLogin(this, e); // 把this物件和引數e傳遞給所有在事件"GoogLogin"
// 中新增的方法,並順序這些方法 (注意,由多路
// 委託特性決定:在使用者程式碼中先新增的方法先執行.
}
}
// 上面對OnGoogLogin方法解釋已經十分詳細了,下面兩個ON方法均與上述ON方法同出一轍.
protected virtual void OnFailedThreeTimes(LoginEventArgs e)
{
if (FailedThreeTimes != null)
{
FailedThreeTimes(this, e);
}
}
protected virtual void OnCancel(System.EventArgs e)
{
if (Cancel != null)
{
Cancel(this, e);
}
}
private void btnLogin_Click(object sender, System.EventArgs e)
// 上面的定義是由VS.NET自動生成,是當按下控制元件的"btnLogin"按鈕時呼叫的方法.
{
if(...)
OnGoodLogin(new LoginEventArgs(txtUserID.Text, txtPass.Text, true));
// 上面一行程式碼呼叫了OnGoodLogin方法,作用是"當控制元件中的按鈕btnLogin被按下時,
// 並且符合上面的if條件時:
// 將透過呼叫OnGoodLogin方法把在使用者程式碼中新增到事件"GoogLogin"中的所有方法
// 全部順序執行一遍.
// 為什麼不在這裡把OnGoodLogin()方法中的程式碼執行一遍,而還要再單獨呼叫OnGoodLogin
// 方法呢? 這是因為有時候使用者程式碼需要重寫OnGoodLogin()方法!
// 下面呼叫的OnFailedThreeTimes()方法和OnCancel()方法解釋同上.
else
OnFailedThreeTimes(new LoginEventArgs(txtUserID.Text, txtPass.Text, false));
}
private void btnCancel_Click(object sender, System.EventArgs e)
{
OnCancel(new EventArgs());
}
}
}
二.呼叫此控制元件的程式集(注意,此程式集是由VS.NET的"新建"->"Windows應用程式"生成的),也就是"使用者程式碼"部分
namespace HostApp
{
public class Form1 : System.Windows.Forms.Form
{
private MyEventTEST.ActiveLogin activeLogin1;
// 上面一行程式碼引用了自定義控制元件庫的類"ActiveLogin",並用它定義了一個物件"activeLogin1".
// 這裡的"MyEventTEST"是在自定義的控制元件庫中定義的名稱空間,如果在這個程式集中沒有出現
// "using MyEventTEST"語句,則該名稱必須出現在對自定義控制元件引用的任何程式碼中!
private System.ComponentModel.Container components = null;
public Form1()
{
InitializeComponent();
}
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
// 上面兩個方法都是又VS.NET自動生成,不做另外解釋.
private void InitializeComponent()
{
this.activeLogin1 = new MyEventTEST.ActiveLogin();
// 上面一行程式碼用自定義控制元件庫中的類"ActiveLogin"例項化物件"activeLogin1"
this.SuspendLayout();
//
// activeLogin1
//
this.activeLogin1.Location = new System.Drawing.Point(144, 8);
this.activeLogin1.Name = "activeLogin1";
this.activeLogin1.Size = new System.Drawing.Size(280, 184);
this.activeLogin1.TabIndex = 0;
this.activeLogin1.GoodLogin += new MyEventTEST.GoodLoginEventHandler(this.activeLogin1_GoodLogin);
this.activeLogin1.Cancel += new System.EventHandler(this.activeLogin1_Cancel);
this.activeLogin1.FailedThreeTimes += new MyEventTEST.FailedThreeTimesEventHandler(this.activeLogin1_FailedThreeTimes);
// !!! 請注意上面的三行程式碼,這是使用者程式碼接受自定義控制元件庫中事件的程式碼 !!!
// 上面三行程式碼分別把使用者定義的方法"activeLogin1_GoodLogin","activeLogin1_Cancel"
// 和"activeLogin1_FailedThreeTimes"分別新增到自定義控制元件中的事件"GoogLogin","Cancel"
// 和"FailedThreeTimes"中; 這樣一來只要自定義控制元件中的對應事件一被激發,這些
// 新增使用者自定義方法就會被執行.
// 要注意的是,使用者自定義方法簽名必須符合對應的多路委託的定義(因為事件是由多路委託
// 定義的,所以要新增到什麼事件,定義就必須符合該事件對應的多路委託的定義)
// 而且,這裡的Cancel事件型別是系統已經定義的多路委託"EventHandler"型別,所以它的例項化
// 與其它兩個事件的例項化有所不同,是"System.EventHandler",而不是"MyEventTEST.EventHandler"!
// 這些使用者自定義方法將在下面列出.
// 不過請注意,上面的三行程式碼雖然在方法InitializeComponent()中,但卻是我們自己手工新增的!
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(440, 357);
this.Controls.AddRange(new System.Windows.Forms.Control[] {
this.activeLogin1,
});
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
}
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
// 上面的方法分別是Windows Forms程式的入口.
private void activeLogin1_GoodLogin(object sender, MyEventTEST.LoginEventArgs e)
{
MessageBox.Show("Good Login! " + e.UserID);
}
private void activeLogin1_FailedThreeTimes(object sender, MyEventTEST.LoginEventArgs e)
{
MessageBox.Show("Failed to login three times.");
}
private void activeLogin1_Cancel(object sender, System.EventArgs e)
{
MessageBox.Show("Cancel");
}
// 上面的三個方法(activeLogin1_GoogLogin,activeLogin1_Cancel和activeLogin1_FailedThreeTimes)
// 就是當自定義控制元件中對應的事件被激發時在當前程式集中對應的處理方法.
// 值得注意的是,簽名應該完全符合對應的多路委託的定義!
}
}
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-993811/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- React原始碼分析 – 事件機制React原始碼事件
- React原始碼分析 - 事件機制React原始碼事件
- .NET Framework 自動記憶體管理機制深入剖析 (C#分析篇) (轉)Framework記憶體C#
- 分析與理解訊息反射機制 (轉)反射
- View事件機制分析View事件
- 深入理解DOM事件機制事件
- Android 事件分發機制的理解Android事件
- View的事件分發機制分析View事件
- 深入理解React:事件機制原理React事件
- 深入理解非同步事件機制非同步事件
- 深入理解事件機制的實現事件
- 深入理解Spring的事件通知機制Spring事件
- 深入理解js Dom事件機制(一)——事件流JS事件
- View事件分發機制分析View事件
- .net的委託和事件的直接理解 (轉)事件
- vscode原始碼分析【五】事件分發機制VSCode原始碼事件
- 完全理解android事件分發機制Android事件
- Netty背後的事件驅動機制Netty事件
- Netty原始碼解析 -- 事件迴圈機制實現原理Netty原始碼事件
- 知乎 node事件機制 轉載事件
- CRM專案事件提醒機制實施要點分析(轉)事件
- Android執行緒間訊息機制-Handler原始碼分析(FrameWork)Android執行緒原始碼Framework
- Android事件分發:從原始碼角度分析View事件分發機制Android事件原始碼View
- 基於原始碼分析 Android View 事件分發機制原始碼AndroidView事件
- 「Android」分析EventBus原始碼擴充套件Weex事件機制Android原始碼套件事件
- vue原始碼解析-事件機制Vue原始碼事件
- [譯]理解 Node.js 事件驅動機制Node.js事件
- Android事件分發機制簡單理解Android事件
- 【轉】理解cookie和session機制CookieSession
- React 為什麼要把事件掛載到 document 上 & 事件機制原始碼分析React事件原始碼
- android的視窗機制分析------事件處理Android事件
- Redis的事件機制Redis事件
- Vue.js原始碼——事件機制Vue.js原始碼事件
- 對epoll機制的學習理解v1
- 談談我對Android安全機制的理解Android
- 淺談JS事件機制與React事件機制JS事件React
- .NET Framework 原始碼Framework原始碼
- JS的事件物件與事件機制JS事件物件