C#代表元及事件觸發(fā) |
代表元是C#中比較復雜的概念,C#中的代表元和C/C++中的函數(shù)指針非常相似使用代表元可以把代表元內(nèi)部方法的引用封裝起來然后通過它使用代表元引用的方法。 它有一個特性就是不需要知道被引用的方法屬于那一個類對象只要函數(shù)的參數(shù)個數(shù)與返回類型與代表元對象一致。這樣說可能比較抽象我下面舉幾個簡單的例子希望能給廣大初學者一些基本的認識
//定義一個返回值為string的無參數(shù)的代表元注意這個代表元只能引用對象中返回值為string的無參數(shù)方法 delegate string MyDelegate(); public class MyClass { public string SayHello() { return "Hello the world!"; }
} public class TestMyClass { public static void Main(string[] args) { MyClass myClass1=new MyClass(); MyDelegate myDelegate1=new MyDelegate(myClass1.SayHello); //下面就使用myDelegate1代替對象myClass1的SayHello方法 System.Console.WriteLine(myDelegate1()); //輸出結果為hello the world! 與調用myClass1.SayHello();效果相同 } } 如果代表元只有這點功能它就沒有什么太大的用處了,代表元還有一個非常有用的功能就是定義復合代表元對象只有同樣類型的代表元才能夠復合起來 + 能定義復合代表元對象 - 從一個復合代表元中去掉一個代表元對象 delegate void MyDelegate(string s); public class MyClass { public void SayHello(string who) { System.Console.WriteLine( who+"hello!"); } public void SayGoodBye(string who) { System.Console.WriteLine( who+"good bye!"); }
}
public class TestMyClass { public static void Main(string[] args) { MyClass myClass1=new MyClass(); MyDelegate myDelegate,myDelegate1; myDelegate=new MyDelegate(myClass1.SayHello); myDelegate1=new MyDelegate(myClass1.SayGoodBye); myDelegate+=myDelegate1; //這樣調用myDeletage就相當于同時調用了myClass1.SayHello和myClass1.SayGoodBye myDelegate("love.net "); //執(zhí)行結果輸出love.net hello! love.net good bye! } } 事件驅動是windows應用程序的重要特征 C#代表元就是用于產(chǎn)生事件,事件就是用于在一個組件中監(jiān)聽這個組件的變化 下面再舉一個簡單的例子 //定義一個事件代理(代表元) public delegate void EventHandler(string str); //定義事件源類 class EventSource { //定義代表元作為事件源類的成員 public event EventHandler Say; public void TriggerEvent() { if(this.Say!=null) //因為Say是個代表元所以執(zhí)行Say方法所做的實際操作由注冊到它的事件處理函數(shù)決定 Say("A event take place!"); } } //測試 class Test { public static void Main() { EventSource aEventSource=new EventSource(); //注冊事件處理函數(shù)為MyEvent 顯示一串字符類似于this.Click+=new EventHandler(Button1_OnClick); aEventSource.Say+=new EventHandler(MyEvent); //此處為演示事件觸發(fā)過程所以就用程序自動觸發(fā) //在圖形界面應用程序中,一般由用戶觸發(fā)事件,后由操作系統(tǒng)發(fā)送消息并調用處理函數(shù) 所以程序員只要注冊事件處理函數(shù) //和編寫事件處理函數(shù)的代碼其他就不用關心了 aEventSource.TriggerEvent(); } //事件處理函數(shù) public static void MyEvent(string str) { System.Console.WriteLine(str); }
}
淺析Visual C#事件處理機制
|
作者: 王凱明
事件簡介:
任何進行過圖形用戶界面開發(fā)的編程人員都會知道事件的概念。當用戶在使用程序的時候,用戶必然要和程序進行一定的交互。比如當用戶點擊窗體上的一個按鈕 后,程序就會產(chǎn)生該按鈕被點擊的事件,并通過相應的事件處理函數(shù)來響應用戶的操作。這樣用戶的直觀感覺就是程序執(zhí)行了我要求的任務了。當然,事件并不一定 是在和用戶交互的情況下才會產(chǎn)生的,系統(tǒng)的內(nèi)部也會產(chǎn)生一些事件并請求處理的,比如時鐘事件就是一個很好例子。不過要介紹C#中的事件處理機制(擴展到更 廣的范圍便是整個.Net框架),我們首先得明白一個叫"委托"的概念。
C#中的委托:
委托,顧名思義,就是中 間代理人的意思。C#中的委托允許你將一個對象中的方法傳遞給另一個能調用該方法的類的某個對象。你可以將類A中的一個方法m(被包含在某個委托中了)傳 遞給一個類B,這樣類B就能調用類A中的方法m了。同時,你還可以以靜態(tài)(static)的方式或是實例(instance)的方式來傳遞該方法。所以這 個概念和C++中的以函數(shù)指針為參數(shù)形式調用其他類中的方法的概念是十分類似的。
委托的概念首先是在Visual J++中被提出來的,現(xiàn)在C#也應用了委托的概念,這也可謂是"拿來主義"吧。C#中的委托是通過繼承System.Delegate中的一個類來實現(xiàn)的,下面是具體的步驟:
1. 聲明一個委托對象,其參數(shù)形式一定要和你想要包含的方法的參數(shù)形式一致。
2. 定義所有你要定義的方法,其參數(shù)形式和第一步中聲明的委托對象的參數(shù)形式必須相同。
3. 創(chuàng)建委托對象并將所希望的方法包含在該委托對象中。
4. 通過委托對象調用包含在其中的各個方法。
以下的C#代碼顯示了如何運用以上的四個步驟來實現(xiàn)委托機制的:
using System; file://步驟1: 聲明一個委托對象 public delegate void MyDelegate(string input);
file://步驟2::定義各個方法,其參數(shù)形式和步驟1中聲明的委托對象的必須相同 class MyClass1{ public void delegateMethod1(string input){ Console.WriteLine( "This is delegateMethod1 and the input to the method is {0}", input); } public void delegateMethod2(string input){ Console.WriteLine( "This is delegateMethod2 and the input to the method is {0}", input); } }
file://步驟3:創(chuàng)建一個委托對象并將上面的方法包含其中 class MyClass2{ public MyDelegate createDelegate(){ MyClass1 c2=new MyClass1(); MyDelegate d1 = new MyDelegate(c2.delegateMethod1); MyDelegate d2 = new MyDelegate(c2.delegateMethod2); MyDelegate d3 = d1 + d2; return d3; } }
file://步驟4:通過委托對象調用包含在其中的方法 class MyClass3{ public void callDelegate(MyDelegate d,string input){ d(input); } } class Driver{ static void Main(string[] args){ MyClass2 c2 = new MyClass2(); MyDelegate d = c2.createDelegate(); MyClass3 c3 = new MyClass3(); c3.callDelegate(d,"Calling the delegate"); } }
C#中的事件處理函數(shù):
C#中的事件處理函數(shù)是一個具有特定參數(shù)形式的委托對象,其形式如下:
public delegate void MyEventHandler(object sender, MyEventArgs e);
其中第一個參數(shù)(sender)指明了觸發(fā)該事件的對象,第二個參數(shù)(e)包含了在事件處理函數(shù)中可以被運用的一些數(shù)據(jù)。上面的MyEventArgs 類是從EventArgs類繼承過來的,后者是一些更廣泛運用的類,如MouseEventArgs類、ListChangedEventArgs類等的 基類。對于基于GUI的事件,你可以運用這些更廣泛的、已經(jīng)被定義好了的類的對象來完成處理;而對于那些基于非GUI的事件,你必須要從 EventArgs類派生出自己的類,并將所要包含的數(shù)據(jù)傳遞給委托對象。下面是一個簡單的例子:
public class MyEventArgs EventArgs{ public string m_myEventArgumentdata; }
在事件處理函數(shù)中,你可以通過關鍵字event來引用委托對象,方法如下:
public event MyEventHandler MyEvent;
現(xiàn)在,我們來創(chuàng)建兩個類,通過這兩個類我們可以知道C#完成事件處理的機制是如何工作的。在我們的實例中,A類將提供事件的處理函數(shù),并在步驟3中創(chuàng)建 委托對象同時將事件處理函數(shù)包含在其中,同上所述,事件處理函數(shù)的參數(shù)形式必須和委托對象的參數(shù)形式相一致。然后,A類將委托對象傳遞給B類。當B類中的 事件被觸發(fā)后,A類中的事件處理函數(shù)就相應的被調用了。下面是示例代碼:
using System; file://步驟1:聲明委托對象 public delegate void MyHandler1(object sender,MyEventArgs e); public delegate void MyHandler2(object sender,MyEventArgs e);
file://步驟2:創(chuàng)建事件處理函數(shù)的方法 class A{ public const string m_id="Class A"; public void OnHandler1(object sender,MyEventArgs e){ Console.WriteLine("I am in OnHandler1 and MyEventArgs is {0}", e.m_id); } public void OnHandler2(object sender,MyEventArgs e){ Console.WriteLine("I am in OnHandler2 and MyEventArgs is {0}", e.m_id); }
file://步驟3:創(chuàng)建委托對象,并事件處理函數(shù)包含在其中同時設置好將要觸發(fā)事件的對象 public A(B b){ MyHandler1 d1=new MyHandler1(OnHandler1); MyHandler2 d2=new MyHandler2(OnHandler2); b.Event1 +=d1; b.Event2 +=d2; } }
file://步驟4:通過委托對象(也就是觸發(fā)事件)來調用被包含的方法 class B{ public event MyHandler1 Event1; public event MyHandler2 Event2; public void FireEvent1(MyEventArgs e){ if(Event1 != null){ Event1(this,e); } } public void FireEvent2(MyEventArgs e){ if(Event2 != null){ Event2(this,e); } } } public class MyEventArgs EventArgs{ public string m_id; } public class Driver{ public static void Main(){ B b= new B(); A a= new A(b); MyEventArgs e1=new MyEventArgs(); MyEventArgs e2=new MyEventArgs(); e1.m_id ="Event args for event 1"; e2.m_id ="Event args for event 2"; b.FireEvent1(e1); b.FireEvent2(e2); } }
C#中的GUI的事件處理函數(shù):
完成GUI下的事件處理函數(shù)的基本方法和上面介紹的并沒有什么多大區(qū)別,下面我們就通過上面的方法來完成一個簡單的實例程序。該實例程序的主類 MyForm類是從Form類繼承過來的。通過觀察整段代碼和相關的注解,你可以發(fā)現(xiàn)我們并沒有給它聲明委托對象并通過event關鍵字來引用該委托對 象,那是因為GUI控件早就幫我們做好了該項工作,其委托對象是System.EventHandler。然而,我們還是要為各個控件定義方法(也就是事 件的處理函數(shù))并將它們包含在創(chuàng)建好的委托對象(System.EventHandler)中。那樣,在用戶和程序進行交互的時候,相應的事件處理函數(shù)就 會被觸發(fā)。具體代碼如下: using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data;
public class MyForm Form{ private Button m_nameButton; private Button m_clearButton; private Label m_nameLabel;
private Container m_components = null;
public MyForm(){ initializeComponents(); } private void initializeComponents(){ m_nameLabel=new Label(); m_nameButton = new Button(); m_clearButton = new Button();
SuspendLayout();
m_nameLabel.Location=new Point(16,16); m_nameLabel.Text="Click NAME button, please"; m_nameLabel.Size=new Size(300,23);
m_nameButton.Location=new Point(16,120); m_nameButton.Size=new Size(176, 23); m_nameButton.Text="NAME"; file://創(chuàng)建委托對象,包含方法并將委托對象賦給按鈕的Click事件 m_nameButton.Click += new System.EventHandler(NameButtonClicked);
m_clearButton.Location=new Point(16,152); m_clearButton.Size=new Size(176,23); m_clearButton.Text="CLEAR"; file://創(chuàng)建委托對象,包含方法并將委托對象賦給按鈕的Click事件 m_clearButton.Click += new System.EventHandler(ClearButtonClicked);
this.ClientSize = new Size(292, 271); this.Controls.AddRange(new Control[] {m_nameLabel, m_nameButton, m_clearButton}); this.ResumeLayout(false); } file://定義方法(事件的處理函數(shù)),其參數(shù)形式必須和委托對象的一致 private void NameButtonClicked(object sender, EventArgs e){ m_nameLabel.Text= "My name is john, please click CLEAR button to clear it"; } private void ClearButtonClicked(object sender,EventArgs e){ m_nameLabel.Text="Click NAME button, please"; } public static void Main(){ Application.Run(new MyForm()); } }
小結:
這樣,我就向大家初步介紹了C#中的事件處理機制。通過本文,希望能使大家對C#中的事件處理機制乃至整個.Net框架的事件處理機制有個大致的了解, 同時還希望大家能明確"委托"這樣的較新的概念。最后還要指出的是,如果你是在用Visual Studio的集成開發(fā)環(huán)境進行開發(fā)的話,那么各個GUI控件會自動幫你生成相關的許多代碼,但是知道了其內(nèi)部的工作機制的話總歸是有很大益處的,對嗎?
C#中動態(tài)創(chuàng)建控件及事件處理程序
using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data;
namespace Miner { /// <summary> /// Summary des cription for Form1. /// </summary> public class Form1 : System.Windows.Forms.Form { private System.Windows.Forms.Panel panel1; /// <summary> /// Required designer variable. /// </summary> ///
private Button[] n =new Button[100]; private int[] kn=new int[100];
private System.ComponentModel.Container components = null;
public Form1() { // // Required for Windows Form Designer support // InitializeComponent();
// // TODO: Add any constructor code after InitializeComponent call // }
/// <summary> /// Clean up any resources being used. /// </summary> protected override void Dispose( bool disposing ) { if( disposing ) { if (components != null) { components.Dispose(); } } base.Dispose( disposing ); }
#region Windows Form Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.panel1 = new System.Windows.Forms.Panel(); this.SuspendLayout(); // // panel1 // this.panel1.Location = new System.Drawing.Point(8, 8); this.panel1.Name = "panel1"; this.panel1.Size = new System.Drawing.Size(400, 400); this.panel1.TabIndex = 0; // // Form1 // this.AutoScaleBaseSize = new System.Drawing.Size(6, 14); this.BackColor = System.Drawing.Color.White; this.ClientSize = new System.Drawing.Size(416, 413); this.Controls.AddRange(new System.Windows.Forms.Control[] { this.panel1}); this.Name = "Form1"; this.Text = "Form1"; this.Load += new System.EventHandler(this.Form1_Load); this.ResumeLayout(false);
} #endregion
/// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main() { Application.Run(new Form1()); }
private void Form1_Load(object sender, System.EventArgs e) { int a=0; int x=0,y=0; for (a=0;a<=99;a++) { n[a] = new Button(); n[a].BackColor =Color.White; n[a].FlatStyle = FlatStyle.Flat; n[a].Width = panel1.Width / 10; n[a].Left = x * n[a].Width; n[a].Height = panel1.Height / 10; n[a].Top = y * n[a].Height; n[a].Name = "b" + a; panel1.Controls.Add(n[a]); panel1.Controls[a].MouseDown += new MouseEventHandler(this.ButtonArray_OnClick); x += 1; if (x == 10) { x = 0; y += 1; } }
}
private void ButtonArray_OnClick(object sender, MouseEventArgs e) { MouseEventArgs arg=(MouseEventArgs)e; Button b1=(Button)sender; if (arg.Button==MouseButtons.Right ) b1.BackColor=Color.White ; else { //b1.BackColor =Color.White ; b1.Image=Image.FromFile("f://my documents//my pictures//elements//regular_smile.gif"); } } } }
C#中的delegate和event
作者: sam1111
在基于Windows平臺的程序設計中,事件(event)是一個很重要的概念。因為在幾乎所有的Windows應用程序中,都會涉及大量的異步調用,比 如響應點擊按鈕、處理Windows系統(tǒng)消息等,這些異步調用都需要通過事件的方式來完成。即使在下一代開發(fā)平臺——.NET中也不例外。
那 么什么是事件呢?所謂事件,就是由某個對象發(fā)出的消息,這個消息標志著某個特定的行為發(fā)生了,或者某個特定的條件成立了。比如用戶點擊了鼠標、 socket上有數(shù)據(jù)到達等。那個觸發(fā)(raise)事件的對象稱為事件的發(fā)送者(event sender),捕獲并響應事件的對象稱為事件的接收者(event receiver)。
在這里,我們將要討論的是,在.NET的主流開發(fā)語言C#中如何使用自定義的事件來實現(xiàn)我們自己的異步調用。
在C#中,事件的實現(xiàn)依賴于delegate,因此我們有必要先了解一下delegate的概念。
Delegate
delegate 是C#中的一種類型,它實際上是一個能夠持有對某個方法的引用的類。與其它的類不同,delegate類能夠擁有一個簽名(signature),并且它 只能持有與它的簽名相匹配的方法的引用。它所實現(xiàn)的功能與C/C++中的函數(shù)指針十分相似。它允許你傳遞一個類A的方法m給另一個類B的對象,使得類B的 對象能夠調用這個方法m。但與函數(shù)指針相比,delegate有許多函數(shù)指針不具備的優(yōu)點。首先,函數(shù)指針只能指向靜態(tài)函數(shù),而delegate既可以引 用靜態(tài)函數(shù),又可以引用非靜態(tài)成員函數(shù)。在引用非靜態(tài)成員函數(shù)時,delegate不但保存了對此函數(shù)入口指針的引用,而且還保存了調用此函數(shù)的類實例的 引用。其次,與函數(shù)指針相比,delegate是面向對象、類型安全、可靠的受控(managed)對象。也就是說,runtime能夠保證 delegate指向一個有效的方法,你無須擔心delegate會指向無效地址或者越界地址。
實現(xiàn)一個delegate是很簡單的,通過以下3個步驟即可實現(xiàn)一個delegate:
1. 聲明一個delegate對象,它應當與你想要傳遞的方法具有相同的參數(shù)和返回值類型。
2. 創(chuàng)建delegate對象,并將你想要傳遞的函數(shù)作為參數(shù)傳入。
3. 在要實現(xiàn)異步調用的地方,通過上一步創(chuàng)建的對象來調用方法。
下面是一個簡單的例子:
using System;
public class MyDelegateTest { // 步驟1,聲明delegate對象 public delegate void MyDelegate(string name);
// 這是我們欲傳遞的方法,它與MyDelegate具有相同的參數(shù)和返回值類型 public static void MyDelegateFunc(string name) { Console.WriteLine("Hello, {0}", name); }
public static void Main() { // 步驟2,創(chuàng)建delegate對象 MyDelegate md = new MyDelegate(MyDelegateTest.MyDelegateFunc);
// 步驟3,調用delegate md("sam1111"); } }
輸出結果是:Hello, sam1111
了解了delegate,下面我們來看看,在C#中對事件是如何處理的。
在C#中處理事件
C#中的事件處理實際上是一種具有特殊簽名的delegate,象下面這個樣子:
public delegate void MyEventHandler(object sender, MyEventArgs e);
其 中的兩個參數(shù),sender代表事件發(fā)送者,e是事件參數(shù)類。MyEventArgs類用來包含與事件相關的數(shù)據(jù),所有的事件參數(shù)類都必須從 System.EventArgs類派生。當然,如果你的事件不含參數(shù),那么可以直接用System.EventArgs類作為參數(shù)。
就是這么簡單,結合delegate的實現(xiàn),我們可以將自定義事件的實現(xiàn)歸結為以下幾步:
1. 定義delegate對象類型,它有兩個參數(shù),第一個參數(shù)是事件發(fā)送者對象,第二個參數(shù)是事件參數(shù)類對象。
2. 定義事件參數(shù)類,此類應當從System.EventArgs類派生。如果事件不帶參數(shù),這一步可以省略。
3. 定義事件處理方法,它應當與delegate對象具有相同的參數(shù)和返回值類型。
4. 用event關鍵字定義事件對象,它同時也是一個delegate對象。
5. 用+=操作符添加事件到事件隊列中(-=操作符能夠將事件從隊列中刪除)。
6. 在需要觸發(fā)事件的地方用調用delegate的方式寫事件觸發(fā)方法。一般來說,此方法應為protected訪問限制,既不能以public方式調用,但可以被子類繼承。名字是OnEventName。
7. 在適當?shù)牡胤秸{用事件觸發(fā)方法觸發(fā)事件。
下面是一個簡單的例子:
using System;
public class EventTest { // 步驟1,定義delegate對象 public delegate void MyEventHandler(object sender, System.EventArgs e);
// 步驟2省略 public class MyEventCls { // 步驟3,定義事件處理方法,它與delegate對象具有相同的參數(shù)和返回值類// 型 public void MyEventFunc(object sender, System.EventArgs e) { Console.WriteLine("My event is ok!"); } }
// 步驟4,用event關鍵字定義事件對象 private event MyEventHandler myevent;
private MyEventCls myecls;
public EventTest() { myecls = new MyEventCls();
// 步驟5,用+=操作符將事件添加到隊列中 this.myevent += new MyEventHandler(myecls.MyEventFunc); }
// 步驟6,以調用delegate的方式寫事件觸發(fā)函數(shù) protected void OnMyEvent(System.EventArgs e) { if(myevent != null)
myevent(this, e); }
public void RaiseEvent() { EventArgs e = new EventArgs();
// 步驟7,觸發(fā)事件 OnMyEvent(e); }
public static void Main() { EventTest et = new EventTest();
Console.Write("Please input 'a':");
string s = Console.ReadLine();
if(s == "a") { et.RaiseEvent(); } else { Console.WriteLine("Error"); } } }
輸出結果如下,黑體為用戶的輸入:
Please input ‘a(chǎn)’: a
My event is ok!
小結
通 過上面的討論,我們大體上明白了delegate和event的概念,以及如何在C#中使用它們。我個人認為,delegate在C#中是一個相當重要的 概念,合理運用的話,可以使一些相當復雜的問題變得很簡單。有時我甚至覺得,delegate甚至能夠有指針的效果,除了不能直接訪問物理地址。而且事件 也是完全基于delegate來實現(xiàn)的。由于能力有限,本文只是對delegate和event的應用作了一個淺顯的討論,并不深入,我希望本文能夠起到 拋磚引玉的作用。真正想要對這兩個概念有更深入的了解的話,還是推薦大家看MSDN。
C#里的委托和事件實現(xiàn)Observer
一、委托的簡介
1、委托的聲明:
<access modifier> delegate <returnType> HandlerName ([parameters])
例如:
public delegate void PrintHandler(string str);
委托聲明定義了一種類型,它用一組特定的參數(shù)以及返回類型來封裝方法。對于靜態(tài)方法,委托對象封裝要調用的方法。對于實例方法,委托對象同時封裝一個實例和該實例上的一個方法。如果您有一個委托對象和一組適當?shù)膮?shù),則可以用這些參數(shù)調用該委托。
2、委托的使用:
using System;
public class MyClass { public static void Main() { PrintStr myPrinter = new PrintStr();
PrintHandler myHandler = null;
myHandler += new PrintHandler(myPrinter.CallPrint); // 將委托鏈接到方法,來實例化委托
if(myHandler!=null)
myHandler("Hello World!"); // 調用委托,相當于匿名調用委托所鏈接的方法
Console.Read(); } }
public delegate void PrintHandler(string str); // 聲明委托類型
public class PrintStr { public void CallPrint(string input) { Console.WriteLine(input); } }
在C#中使用委托方法:
· 創(chuàng)建委托所使用的方法必須和委托聲明相一致(參數(shù)列表、返回值都一致)
· 利用 +=、-=來進行委托的鏈接、取消鏈接或直接使用Delegate.Combine和Delegate.Remove方法來實現(xiàn)
· 可以使用MulticastDelegate的實例方法GetInvocationList()來獲取委托鏈中所有的委托
· 不能撰寫包含 out 參數(shù)的委托
二、事件的簡介
C# 中的“事件”是當對象發(fā)生某些事情時,類向該類的客戶提供通知的一種方法。
1、事件的聲明:
聲明的格式為:<access modifier> event <delegate type> EventName
因為使用委托來聲明事件,所以在類里聲明事件時,首先必須先聲明該事件的委托類型<delegate type>(如果尚未聲明的話)。在上面我們已經(jīng)提到過了委托類型的聲明,但是在.net framework下為事件使用的委托類型進行聲明時 有更嚴格的規(guī)定:
(1)、 事件的委托類型應采用兩個參數(shù);
(2)、兩個參數(shù)分別是:指示事件源的“對象源”參數(shù)和封裝事件的其他任何相關信息的“e”參數(shù);
(3)、“e”參數(shù)的類型應為EventArgs 類或派生自 EventArgs 類。
如下的定義:
public delegate void PrintHandler(object sender,System.EventArgs e);
然后我們才能聲明該委托類型的事件
例如:
public event PrintHandler Print;
當事件發(fā)生時,將調用其客戶提供給它的委托。
2、調用事件:
類聲明了事件以后,可以就像處理所指示的委托類型的字段那樣處理該事件。如果沒有任何客戶將委托與該事件綁定,則該字段將為空;否則該字段引用應在調用該 事件時調用的委托。因此,調用事件時通常先檢查是否為空,然后再調用事件。(調用事件,即觸發(fā)事件,只能從聲明該事件的類內(nèi)進行)
if(Print != null) { Print (this,e); }
3、事件綁定:
從類的外面來看,事件就象類的一個公共成員,通過 類名.事件名 的形式來訪問,但是只能對它做綁定和解除綁定的操作,而不能有其他操作。
類名. Print += new PrintHandler(綁定的方法名) // 將某個方法綁定到Print事件上
類名. Print -= new PrintHandler(綁定的方法名) // 將某個已綁定到Print事件上的方法從Print事件上解除
三、委托和事件的使用
委托和事件在用戶界面程序里用的比較的多,比如象在winform或webform的用戶UI上的button和它的click事件:
// 將Button1_Click()方法綁定到按鈕控件Button1的Click事件上
this.Button1.Click += new System.EventHandler(this. Button1_Click);
private void Button1_Click(object sender, System.EventArgs e) // Button1_Click()方法
{ …… }
然 而除了用戶界面程序外,在很多其他地方也用到了事件驅動模式,比如觀察者模式(Observer)或發(fā)布/訂閱(Publish/Subscribe) 里:在一個類里發(fā)布(Publish)某個可以被觸發(fā)的事件,而其他的類就可以來訂閱(Subscribe)該事件。一旦這個發(fā)布者類觸發(fā)了該事件,那么 運行時環(huán)境會立刻告知所有訂閱了該事件的訂閱者類:這個事件發(fā)生了!從而各個訂閱者類可以作出它們自己的反應(調用相應方法)。
我們來舉一個生活中的實際例子來說明如何使用委托和事件,以及使用委托和事件所帶來的好處:
比如說有一個公司(場景),你是老板,手下有主管和員工,作為老板你會指派(委托)主管管理員工的工作,如果某個員工玩游戲,則讓某個主管從該員工的薪水里扣去500元錢。
這就是現(xiàn)實中的委托。
而在寫程序中,假設程序員就是老板,有兩個類分別為主管和員工,而主管小王和員工小張就是兩個類的對象實例。員工類有一個方法:玩游戲,同時就有一個玩游戲的事件,他一玩游戲就會激發(fā)這個事件。而主管類就是負責處理該事件的,他負責把玩游戲的員工的薪水扣除500。
(一)、首先,我們來看看在非委托的情況下比較常見的一種設計方式(當然這不是唯一的方式,也不是最好的方式,但是很常見):
using System;
namespace CSharpConsole { public class 場景 { [STAThread]
public static void Main(string[] args) { Console.WriteLine("場景開始了.");
// 生成主管類的對象實例 小王
主管 小王 = new 主管();
// 生成員工類的對象實例 小張,指定他的主管
員工 小張 = new 員工(小王);
Console.WriteLine("該員工本有的薪水:" + 小張.薪水.ToString());
// 員工開始玩游戲
小張.玩游戲();
Console.WriteLine("現(xiàn)在該員工還剩下:" +小張.薪水.ToString());
Console.WriteLine("場景結束");
Console.ReadLine(); }
}
// 負責扣錢的人----主管
public class 主管 { public 主管() { Console.WriteLine("生成主管"); }
public void 扣薪水(員工 employee) { Console.WriteLine("主管:好小子,上班時間膽敢玩游戲");
Console.WriteLine("主管:看看你小子有多少薪水");
Console.WriteLine("開始扣薪水...");
System.Threading.Thread.Sleep(1000);
employee.薪水 = employee.薪水 - 500;
Console.WriteLine("扣薪水執(zhí)行完畢."); } }
// 如果玩游戲,則會引發(fā)事件
public class 員工 { // 保存員工的薪水
private int m_Money;
// 保存該員工的主管
private 主管 m_Manager;
public 員工(主管 manager) { Console.WriteLine("生成員工.");
m_Manager = manager; // 通過構造函數(shù),初始化員工的主管。
m_Money = 1000; // 通過構造函數(shù),初始化員工的薪水。 }
public int 薪水 // 此屬性可以操作員工的薪水 。 { get { return m_Money; }
set { m_Money = value; } }
public void 玩游戲() { Console.WriteLine("員工開始玩游戲了..");
Console.WriteLine("員工:CS真好玩,哈哈哈! 我玩...");
System.Threading.Thread.Sleep(1000);
m_Manager. 扣薪水(this); } } }
這種方法所帶來的問題: 員工類和主管類的耦合性太高
1、 在客戶程序里必須先創(chuàng)建了主管類之后才能生成員工類,如果在不需要主管類對象而只需員工類對象的地方,為了創(chuàng)建所需的員工類對象實例,你也不得不去先創(chuàng)建一個主管類的對象實例;
2、 如果場景劇本(即客戶程序需求)發(fā)生了變化
(1)、現(xiàn)在要讓一個新的角色(一個新的類),如保安,來代替主管,負責在員工玩游戲時扣員工薪水,那么我們不得不去修改員工類,或許還需要修改主管類;
(2)、如果場景劇本增加新的需求,要求員工在玩游戲后,不但要扣薪水,還要在績效上扣分,那么我們也不得不修改員工類。
(二)、利用委托的實現(xiàn):
下面有個例子:在C# 控制臺應用程序編輯運行成功:
using System;
namespace CSharpConsole { // 定義委托
public delegate void PlayGameHandler(object sender,System.EventArgs e);
// 負責扣錢的人----主管
public class 主管 { public 主管() { Console.WriteLine("生成主管"); }
public void 扣薪水(object sender,EventArgs e) { Console.WriteLine("主管:好小子,上班時間膽敢玩游戲");
Console.WriteLine("主管:看看你小子有多少薪水");
員工 employee = (員工)sender;
Console.WriteLine("開始扣薪水...");
System.Threading.Thread.Sleep(1000);
employee.薪水 = employee.薪水 - 500;
Console.WriteLine("扣薪水執(zhí)行完畢."); } }
// 如果玩游戲,則會引發(fā)事件
public class 員工 { // 先定義一個事件,這個事件表示員工在玩游戲。
public event PlayGameHandler PlayGame;
// 保存員工薪水的變量
private int m_Money;
public 員工() { Console.WriteLine("生成員工.");
m_Money = 1000; // 構造函數(shù),初始化員工的薪水。 }
public int 薪水 // 此屬性可以操作員工的薪水 。 { get { return m_Money; }
set { m_Money = value; } }
public void 玩游戲() { Console.WriteLine("員工開始玩游戲了..");
Console.WriteLine("員工:CS真好玩,哈哈哈! 我玩...");
System.Threading.Thread.Sleep(1000);
System.EventArgs e = new EventArgs();
OnPlayGame(e); }
protected virtual void OnPlayGame(EventArgs e) { if(PlayGame != null) { PlayGame(this,e); } } }
public class 場景 { [STAThread]
public static void Main(string[] args) { Console.WriteLine("場景開始了.");
// 生成主管類的對象實例 小王
主管 小王 = new 主管();
// 生成員工類的對象實例 小張
員工 小張 = new 員工();
// 設下委托,指定監(jiān)視
小張.PlayGame += new PlayGameHandler(小王. 扣薪水);
Console.WriteLine("該員工本有的薪水:" + 小張.薪水.ToString());
// 員工開始玩游戲
小張.玩游戲();
Console.WriteLine("現(xiàn)在該員工還剩下:" +小張.薪水.ToString());
Console.WriteLine("場景結束");
Console.ReadLine(); } } }
對于前面提出的問題:
1、 解耦了主管類和員工類之間的必然聯(lián)系,可以單獨創(chuàng)建員工類對象實例,而不用管是否有主管類對象實例的存在;
2、 在客戶程序需求變化時:
(1)、我們只需修改客戶程序,即上面例子里的class 場景,將委托改為如下:
保安 小李 = new 保安();
小張.PlayGame += new PlayGameHandler(小李. 扣薪水);
即可實現(xiàn)由保安來負責扣薪水的需求變化,而不用動員工類。
(2)、我們只需修改客戶程序,即上面例子里的class 場景,添加一個如下的委托:
小張.PlayGame += new PlayGameHandler(某某. 扣績效分);
這個“某某”可以是主管,也可以是其他新的角色(新的類),只需要在“某某”對應的類里定義扣績效分的動作即可,而不用動員工類。
四、總結:
當然,不使用委托和事件我們?nèi)匀豢梢栽O計出解耦的類,然而卻會增加很多的類、接口以及關聯(lián)等等,增加了代碼量和程序的邏輯復雜性,而在.net里利用委托和事件我們只需少的多的代碼來實現(xiàn)。
委托和事件的使用有如下幾個要素:
1、激發(fā)事件的對象-----就是員工小張 2、處理對象事件的對象-----就是主管小王 3、定義委托,就是你讓主管小王監(jiān)視員工小張。
如果這三個要素都滿足的話,則你就寫出了一個完整事件的處理。
|