委托
當(dāng)要把方法作為實參傳送給其他方法的形參時,形參需要使用委托。委托是一個類型,是一個函數(shù)指針類型,這個類型將該委托的實例化對象所能指向的函數(shù)的細(xì)節(jié)封裝起來了,即規(guī)定了所能指向的函數(shù)的簽名,也就是限制了所能指向的函數(shù)的參數(shù)和返回值。當(dāng)實例化委托的時候,委托對象會指向某一個匹配的函數(shù),實質(zhì)就是將函數(shù)的地址賦值給了該委托的對象,然后就可以通過該委托對象來調(diào)用所指向的函數(shù)了。
定義委托
語法如下:
delegate result-type Identifier ([parameters]);
說明:
result-type:返回值的類型,和方法的返回值類型一致
Identifier:委托的名稱
parameters:參數(shù),要引用的方法帶的參數(shù)
小結(jié):
當(dāng)定義了委托之后,該委托的對象一定可以而且也只能指向該委托所限制的函數(shù)。即參數(shù)的個數(shù)、類型、順序都要匹配,返回值的類型也要匹配。
因為定義委托相當(dāng)于是定義一個新類,所以可以在定義類的任何地方定義委托,既可以在一個類的內(nèi)部定義,那么此時就要通過該類的類名來調(diào)用這個委托(委托必須是public、internal),也可以在任何類的外部定義,那么此時在命名空間中與類的級別是一樣的。
聲明一個基于某個委托的事件
Event delegateName eventName;
eventName不是一個類型,而是一個具體的對象,這個具體的對象只能在類A內(nèi)定義而不能在類A外定義。
在類A中定義一個觸發(fā)該事件的方法
ReturnType FunctionName([parameters]) { …… If(eventName != null) { eventName([parameters]); 或者eventName.Invoke([parameters]); } …… }
觸發(fā)事件之后,事件所指向的函數(shù)將會被執(zhí)行。這種執(zhí)行是通過事件名稱來調(diào)用的,就像委托對象名一樣的。
觸發(fā)事件的方法只能在A類中定義,事件的實例化,以及實例化之后的實現(xiàn)體都只能在A類外定義。
程序?qū)嵗?/strong>
using System; using System.Collections.Generic; using System.Text; using System.Threading; using System.Windows.Forms; namespace DelegateStudy { public delegate void DelegateClick (int a); public class butt { public event DelegateClick Click; public void OnClick(int a) { if(Click != null) Click.Invoke(a); //Click(a);//這種方式也是可以的 MessageBox.Show("Click();"); } } class Frm { public static void Btn_Click(int a) { for (long i = 0; i < a; i++) Console.WriteLine(i.ToString()); } static void Main(string[] args) { butt b = new butt(); //在委托中,委托對象如果是null的,直接使用+=符號,會報錯,但是在事件中,初始化的時候,只能用+= b.Click += new DelegateClick (Fm_Click); //事件是基于委托的,所以委托推斷一樣適用,下面的語句一樣有效:b.Click += Fm_Click; //b.Click(10);錯誤:事件“DelegateStudy.butt.Click”只能出現(xiàn)在 += 或 -= 的左邊(從類型“DelegateStudy.butt”中使用時除外) b.OnClick (10000); MessageBox.Show("sd234234234"); Console.ReadLine(); } } }
控件事件委托EventHandler
在控件事件中,有很多的委托,在這里介紹一個最常用的委托EventHandler,.NET Framework中控件的事件很多都基于該委托,EventHandler委托已在.NET Framework中定義了。它位于System命名空間:
Public delegate void EventHandler(object sender,EventArgs e);
委托EventHandler參數(shù)和返回值
事件最終會指向一個或者多個函數(shù),函數(shù)要與事件所基于的委托匹配。事件所指向的函數(shù)(事件處理程序)的命名規(guī)則:按照約定,事件處理程序應(yīng)遵循“object_event”的命名約定。object就是引發(fā)事件的對象,而event就是被引發(fā)的事件。從可讀性來看,應(yīng)遵循這個命名約定。
首先,事件處理程序總是返回void,事件處理程序不能有返回值。其次是參數(shù),只要是基于EventHandler委托的事件,事件處理程序的參數(shù)就應(yīng)是object和EventArgs類型:
第一個參數(shù)接收引發(fā)事件的對象,比如當(dāng)點擊某個按鈕的時候,這個按鈕要觸發(fā)單擊事件最終執(zhí)行這個函數(shù),那么就會把當(dāng)前按鈕傳給sender,當(dāng)有多個按鈕的單擊事件都指向這個函數(shù)的時候,sender的值就取決于當(dāng)前被單擊的那個按鈕,所以可以為幾個按鈕定義一個按鈕單擊處理程序,接著根據(jù)sender參數(shù)確定單擊了哪個按鈕:
if(((Button)sender).Name =="buttonOne")
第二個參數(shù)e是包含有關(guān)事件的其他有用信息的對象。
控件事件的其他委托
控件事件還有其他的委托,比如在窗體上有與鼠標(biāo)事件關(guān)聯(lián)的委托:
Public delegate void MouseEventHandler(object sender,MouseEventArgs e);
public event MouseEventHandler MouseDown;
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseDown);
private void Form1_MouseDown(object sender, MouseEventArgs e){};
MouseDown事件使用MouseDownEventArgs,它包含鼠標(biāo)的指針在窗體上的的X和Y坐標(biāo),以及與事件相關(guān)的其他信息。
控件事件中,一般第一個參數(shù)都是object sender,第二個參數(shù)可以是任意類型,不同的委托可以有不同的參數(shù),只要它派生于EventArgs即可。
程序?qū)嵗?/strong>
using System; using System.Collections.Generic; using System.Text; using System.Threading; namespace SecondChangeEvent1 { // 該類用來存儲關(guān)于事件的有效信息外, // 還用來存儲額外的需要傳給訂閱者的Clock狀態(tài)信息 public class TimeInfoEventArgs : EventArgs { public TimeInfoEventArgs(int hour,int minute,int second) { this.hour = hour; this.minute = minute; this.second = second; } public readonly int hour; public readonly int minute; public readonly int second; } // 定義名為SecondChangeHandler的委托,封裝不返回值的方法, // 該方法帶參數(shù),一個clock類型對象參數(shù),一個TimeInfoEventArgs類型對象 public delegate void SecondChangeHandler( object clock, TimeInfoEventArgs timeInformation ); // 被其他類觀察的鐘(Clock)類,該類發(fā)布一個事件:SecondChange。觀察該類的類訂閱了該事件。 public class Clock { // 代表小時,分鐘,秒的私有變量 int _hour; public int Hour { get { return _hour; } set { _hour = value; } } private int _minute; public int Minute { get { return _minute; } set { _minute = value; } } private int _second; public int Second { get { return _second; } set { _second = value; } } // 要發(fā)布的事件 public event SecondChangeHandler SecondChange; // 觸發(fā)事件的方法 protected void OnSecondChange( object clock, TimeInfoEventArgs timeInformation ) { // Check if there are any Subscribers if (SecondChange != null) { // Call the Event SecondChange(clock, timeInformation); } } // 讓鐘(Clock)跑起來,每隔一秒鐘觸發(fā)一次事件 public void Run() { for (; ; ) { // 讓線程Sleep一秒鐘 Thread.Sleep(1000); // 獲取當(dāng)前時間 System.DateTime dt = System.DateTime.Now; // 如果秒鐘變化了通知訂閱者 if (dt.Second != _second) { // 創(chuàng)造TimeInfoEventArgs類型對象,傳給訂閱者 TimeInfoEventArgs timeInformation = new TimeInfoEventArgs( dt.Hour, dt.Minute, dt.Second); // 通知訂閱者 OnSecondChange(this, timeInformation); } // 更新狀態(tài)信息 _second = dt.Second; _minute = dt.Minute; _hour = dt.Hour; } } } /* ======================= Event Subscribers =============================== */ // 一個訂閱者。DisplayClock訂閱了clock類的事件。它的工作是顯示當(dāng)前時間。 public class DisplayClock { // 傳入一個clock對象,訂閱其SecondChangeHandler事件 public void Subscribe(Clock theClock) { theClock.SecondChange += new SecondChangeHandler(TimeHasChanged); } // 實現(xiàn)了委托匹配類型的方法 public void TimeHasChanged( object theClock, TimeInfoEventArgs ti) { Console.WriteLine("Current Time: {0}:{1}:{2}", ti.hour.ToString(), ti.minute.ToString(), ti.second.ToString()); } } // 第二個訂閱者,他的工作是把當(dāng)前時間寫入一個文件 public class LogClock { public void Subscribe(Clock theClock) { theClock.SecondChange += new SecondChangeHandler(WriteLogEntry); } // 這個方法本來應(yīng)該是把信息寫入一個文件中 // 這里我們用把信息輸出控制臺代替 public void WriteLogEntry( object theClock, TimeInfoEventArgs ti) { Clock a = (Clock)theClock; Console.WriteLine("Logging to file: {0}:{1}:{2}", a.Hour.ToString(), a.Minute.ToString(), a.Second.ToString()); } } /* ======================= Test Application =============================== */ // 測試擁有程序 public class Test { public static void Main() { // 創(chuàng)建clock實例 Clock theClock = new Clock(); // 創(chuàng)建一個DisplayClock實例,讓其訂閱上面創(chuàng)建的clock的事件 DisplayClock dc = new DisplayClock(); dc.Subscribe(theClock); // 創(chuàng)建一個LogClock實例,讓其訂閱上面創(chuàng)建的clock的事件 LogClock lc = new LogClock(); lc.Subscribe(theClock); // 讓鐘跑起來 theClock.Run(); } } }
MouseEventHandler和EventHandler傳遞參數(shù)的局限性分析
開發(fā)過程中,特別是使用自定義控件時,常常需要對一個控件的click,mouseDown,mouseUp等事件的處理進行重新定義,以滿足實際工程應(yīng)用和要求。常用的方法如下:
button1.Click -= new EventHandler(ButtonClick_Handler); button1.MouseUp -= new MouseEventHandler(ButtonUp_Handler); button1.Click += new EventHandler(ButtonClick_Handler); MouseUp += new MouseEventHandler(ButtonUp_Handler);
可以看到,這里是通過EventHandler和MouseEventHandler這兩個委托來能click和mouseup賦值。
這兩個委托的定義如下:
EventHandler:
.NET Framework 中的事件模型基于具有事件委托,該委托將事件與事件處理程序連接。引發(fā)事件需要兩個元素:
標(biāo)識對事件提供響應(yīng)的方法的委托。
保存事件數(shù)據(jù)的類。
public delegate void EventHandler(Object sender, EventArgs e); public event EventHandler NoDataEventHandler;
MouseEventHandler:
表示將處理窗體、控件或其他組件的 MouseDown、MouseUp 或 MouseMove 事件的方法。
委托的原型:
public delegate void MouseEventHandler( Object sender, MouseEventArgs e )
這兩個委托都有兩個參數(shù),其中Sender可以通過.net的機制來捕獲,而EventArgs和MouseEventArgs 該如何使用呢?或者說如何給它賦值?暫時沒有辦法,還請高人指點。
其實這個問題可以通過匿名委托來解決。
2、使用匿名委托給一些EventHandler/MouseEventHandler的方法傳參數(shù)
關(guān)鍵代碼如下:
public void setSeatButtonMove_EventHandler(CSeatButton seatBtn, Object parentForm) { ///* 常規(guī)事件加載方式 */ //seatBtn.button1.Click -= new EventHandler(seatButtonClick_Handler); //seatBtn.button1.MouseUp -= new MouseEventHandler(seatButtonUp_Handler); //seatBtn.button1.Click += new EventHandler(seatButtonClick_Handler); //seatBtn.button1.MouseUp += new MouseEventHandler(seatButtonUp_Handler); /* 匿名事件加載方式 */ seatBtn.button1.Click -= delegate(Object o, EventArgs e) { seatButtonClick_Handler(seatBtn.button1, parentForm); }; seatBtn.button1.MouseUp -= delegate(Object o, MouseEventArgs e) { seatButtonUp_Handler(seatBtn.button1, parentForm); }; seatBtn.button1.Click += delegate(Object o, EventArgs e) { seatButtonClick_Handler(seatBtn.button1, parentForm); }; seatBtn.button1.MouseUp += delegate(Object o, MouseEventArgs e) { seatButtonUp_Handler(seatBtn.button1, parentForm); }; } public void seatButtonClick_Handler(object sender,object formOfSender) { string formName = ((Form)formOfSender).Name.Trim(); if (formName.Equals("Form1")) { MessageBox.Show("In Form1,click a button!"); } if (formName.Equals("Form2")) { MessageBox.Show("In Form2,click a button!"); } }
小結(jié)
(1)、在定義事件的那個類A里面,可以任意的使用事件名,可以觸發(fā);在別的類里面,事件名只能出現(xiàn)在 += 或-= 的左邊來指向函數(shù),即只能實例化,不能直接用事件名觸發(fā)。但是可以通過A類的對象來調(diào)用A類中的觸發(fā)事件的函數(shù)。這是唯一觸發(fā)事件的方式。
(2)、不管是多播委托還是單播委托,在沒有特殊處理的情況下,在一個線程的執(zhí)行過程中去調(diào)用委托(委托對象所指向的函數(shù)),調(diào)用委托的執(zhí)行是不會新起線程的,這個執(zhí)行還是在原線程中的,這個對于事件也是一樣的。當(dāng)然,如果是在委托所指向的函數(shù)里面去啟動一個新的線程那就是另外一回事了。
(3)、事件是針對某一個具體的對象的,一般在該對象的所屬類A中寫好事件,并且寫好觸發(fā)事件的方法,那么這個類A就是事件的發(fā)布者,然后在別的類B里面定義A的對象,并去初始化該對象的事件,讓事件指向B類中的某一個具體的方法,B類就是A類事件的訂閱者。當(dāng)通過A類的對象來觸發(fā)A類的事件的時候(只能A類的對象來觸發(fā)A類的事件,別的類的對象不能觸發(fā)A類的事件,只能訂閱A類的事件,即實例化A類的事件),作為訂閱者的B類會接收A類觸發(fā)的事件,從而使得訂閱函數(shù)被執(zhí)行。一個發(fā)布者可以有多個訂閱者,當(dāng)發(fā)布者發(fā)送事件的時候,所有的訂閱者都將接收到事件,從而執(zhí)行訂閱函數(shù),但是即使是有多個訂閱者也是單線程。
如對本文有疑問,請?zhí)峤坏浇涣髡搲?,廣大熱心網(wǎng)友會為你解答??! 點擊進入論壇