一:什么是事件
事件是可以被控件識別的操作,如按下確定按鈕,選擇某個單選按鈕或者復選框。每一種控件有自己可以識別的事件,如窗體的加載、單擊、雙擊等事件,編輯框(文本框)的文本改變事件,等等。事件在桌面應用程序里面無處可見,比如winform,WPF。。。,其次事件是基于委托而產生的。
二:事件的基本使用
1.事件的聲明: 其實和委托一樣只是多了一個Event而已。ShowMsg 就具備了ShowMsgHandler的功能。
Notes: 1. 委托可以依賴于一個類或者一個域名空間(C#基礎---委托的使用,里面我有提到過), 而event必須依賴于一個類。否者無法聲明。
2. 委托可以用【=號】,而事件中只能用【+】或者【-】實現對方法的添加和刪除。當事件為空的時候調用【-】方法不會報錯。
public delegate void ShowMsgHandler(string str);public event ShowMsgHandler ShowMsg;
2.事件基本使用: 其實基本用法和委托差不多。這里有一點要說明,其實可以通過判斷是否為Null,來確定是否已經注冊了方法到事件或者委托。這個有時候勇于判斷事件是否該觸發。其次也發現事件第一次添加方法的時候是直接使用【+】號的,而不用像委托那樣第一次使用【=】號,后面的才使用【+】號?! ?nbsp;
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace SpongeBobCoder.EventTest{ public delegate void ShowMsgHandler(string str); public class PRogram { public static event ShowMsgHandler ShowMsg; public static void ShowName(string str) { Console.WriteLine("My Name is {0}", str); } public static void Main(string[] args) { Console.WriteLine(ShowMsg == null); ShowMsg += ShowName; ShowMsg("SopongeBob"); Console.WriteLine(ShowMsg == null); Console.ReadKey(); } }}
3. 為何使用事件:
其實從上面來看事件和委托差不多。用法沒啥區別,但是為何還要使用委托呢? 在Codeplex看到的一篇文檔感覺挺不錯的。http://www.codeproject.com/Articles/7316/Events-and-Delegates ;小弟英語不好,英語好的就去看看原文,我的大概理解是: 好比一個App由一個項目組在開發,必定有一個項目經理,而這個項目經理的下面會有很多幫他做事的員工,其實經理就好比是一個委托,而每一位工作的員工就好比委托注冊的方法。項目開發完成了,拿給了用戶,用戶安裝好了??墒怯脩舭l現軟件有缺陷,需要改進,而往往這個時候用戶是不會直接跟開發組的項目經理接觸的。往往會把意見給 維護部門,而由維護部門來告知相應開發組的項目經理。而通知這段過程就叫做事件。事件可以用來更好的封裝,和管理委托的。個人理解,這就是事件為何基于特定類的。不同類的 事件可以綁定同一個委托,從而注冊不同的方法。
3.1 先什么一個Publisher類:類中有一個事件CalculatorEvent 和一個方法DoSomething,若事件被添加方法后,那么將執行
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace SpongeBobCoder.EventTest{ public class Publisher { public event CalculatorHandler CalculatorEvent; public void DoSomething(double num1,double num2) { if (CalculatorEvent != null) { CalculatorEvent(num1, num2); } } }}
3.2 然后看Program類,Main方法里面聲明了兩個Publisher對象,分別是A,B。分別添加了AddNum和SumNum,運行結果是 3 和 -1。通過一個Publisher類可以對委托進行管理。
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace SpongeBobCoder.EventTest{ public delegate void CalculatorHandler(double num1,double num2); public class Program { public static void AddNum(double num1, double num2) { Console.WriteLine("兩數之和為:{0}", num1 + num2); } public static void SubNum(double num1, double num2) { Console.WriteLine("兩數之差為:{0}", num1-num2); } public static void Main(string[] args) { Publisher pubA = new Publisher(); Publisher pubB = new Publisher(); pubA.CalculatorEvent += AddNum; pubB.CalculatorEvent += SubNum; pubA.DoSomething(1, 2); pubB.DoSomething(1, 2); Console.ReadKey(); } }}
三:事件的使用
1.異常處理: 事件可以注冊多個方法,可是要是其中有一個方法拋出異常了怎么辦,一旦拋出異常了。那么當前執行程序被中斷,那么后面注冊的方法就沒法執行了。問題來了,那些方法可以解決這個問題呢? 方法一,保證所注冊的方法不會出現異常,這一塊我們是無法預知。 方法二。將注冊的方法的異常都吃掉,其中微軟提供了兩個方法GetInvocationList和DynamicInvoke方法:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace SpongeBobCoder.EventTest{ public class Publisher { public event CalculatorHandler CalculatorEvent; public void DoSomething(double num1, double num2) { if (CalculatorEvent != null) { Delegate[] delArray = CalculatorEvent.GetInvocationList(); //獲取到所有的委托方法. foreach (Delegate del in delArray) { try { object obj = del.DynamicInvoke(num1, num2); //obj是獲取每個方法的返回值,如果聲明的是無返回值的委托,那么obj==null Console.WriteLine(obj == null); } catch (Exception e) // 把異常吃掉 { Console.WriteLine(e.InnerException.Message); } } } } }}
2.異步調用:對于前面的注冊的時間,都是順序執行,那如何實現異步執行呢,各個注冊之間不相互干擾,其實微軟提供了一個BeginInvoke方法可以解決這個問題.
Public IAsyncResult BeginInvoke ( InvokeArgs invokeArgs, // 這一部分對于的是委托 AsyncCallback callback,// 回調方法,注冊方法執行完后,將會執行回調方法 Object userState // 傳遞的參數)
上面理解起來可能有點困難下面來看看代碼吧:
Program類: AddNum 方法有5秒的延時。SubNum沒有添加延時,注冊順序是 AddNum,SubNum
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Threading;namespace SpongeBobCoder.EventTest{ public delegate double CalculatorHandler(double num1, double num2); public class Program { public static double AddNum(double num1, double num2) { Thread.Sleep(TimeSpan.FromSeconds(5)); Console.WriteLine("兩數之和為:{0}", num1 + num2); return num1 + num2; } public static double SubNum(double num1, double num2) { Console.WriteLine("兩數之差為:{0}", num1 - num2); return num1 - num2; } public static void Main(string[] args) { Publisher pubA = new Publisher(); pubA.CalculatorEvent += AddNum; pubA.CalculatorEvent += SubNum; pubA.DoSomething(1, 5); Console.ReadKey(); } }}
publisher類: 注意看紅色方法,前面兩個參數是與委托對應的。后面的MyCallBack是回調的方法,
using System;using System.Collections.Generic;using System.Linq;using System.Runtime.Remoting.Messaging;using System.Text;using System.Threading.Tasks;namespace SpongeBobCoder.EventTest{ public class Publisher { public event CalculatorHandler CalculatorEvent; public void DoSomething(double num1, double num2) { if (CalculatorEvent != null) { Delegate[] delArray = CalculatorEvent.GetInvocationList(); //獲取到所有的委托方法. foreach (Delegate del in delArray) { try { CalculatorHandler handler = del as CalculatorHandler; IAsyncResult myResult = handler.BeginInvoke(num1, num2, MyCallback, "方法執行完畢,回調成功" + handler.Method.Name); // Console.WriteLine("SpongeBob"); 這塊代碼不注釋的話是先執行這段代碼的輸出,然后才會輸出其他的。這一塊我不知道為什么 } catch (Exception e) // 把異常吃掉 { Console.WriteLine(e.InnerException.Message); } } } } public void MyCallback(IAsyncResult asyncResult) { AsyncResult result = (AsyncResult)asyncResult; CalculatorHandler handler = (CalculatorHandler)result.AsyncDelegate; Console.WriteLine(asyncResult.AsyncState); Console.WriteLine("獲取到執行結果為:{0} /n", handler.EndInvoke(asyncResult)); } }}
運行結果: 其實先執行的是 SubNum,已經達到了異步的效果,其中通過EndInvoke也在回調函數中獲取到了委托的返回值。
codezip:http://files.VEVb.com/FourLeafCloverZc/CSharp.zip
總結: 以前最開始的對事件理解不清楚,記得當時在winform的時候跨線程獲取數據就要用Invoke來獲取,不然老是提示線程不安全。本次博客中我發現了兩個問題需要個各位博友幫忙解答
1. 在異步調用的代碼中有一段代碼被我注解了。其實我發現了一個問題, 如果 Console.WriteLine("SpongeBob"); 不注解,運行的情況是,先輸出兩行(“SpongeBob”) 然后再輸出 上圖的運行結果。不清楚是什么原因,求大神指點。
2. 對于下面代碼如果調用的傳入的參數是1,0 ; 是不會報錯的。運行結果是兩數相除無窮大。應該會報除數不能為0的異常呀。
public static void DivNum(double num1, double num2) { Console.WriteLine("兩數相除為:{0}", num1 / num2); }
新聞熱點
疑難解答