上一篇那些年困擾我們的委托(C#)講了委托,這一篇自然就輪到事件了。
不喜歡官方的表達方式,喜歡按照自己的想法去理解一些抽象的東西,我是一個喜歡簡單怕麻煩的人。
考慮到委托使用的一些缺陷,就有了事件。委托是不安全的,打個比方,如果把委托當作共有字段,那麼事件就相當於是屬性的概念。
事件就是被限制使用的委托變量,事件裡面封裝了一個多播委托。
事件語法:public event 委托類型 事件名;
事件的作用:事件的作用與委托變量一樣,只是功能上比委托變量有更多的限制。比如:只能通過+=或者-=來綁定方法。只能在類內部調用事件。
當一個結果發生時,有可能引起另外的一些反應,這就好像因果關系。而事件則是這個因與果的內部聯系。
事件的本質:委托的一個實例,添加了event關鍵字修飾。
委托是一種類型,事件是委托類型的實例。
和委托的區別:
整個windows系統都是通過事件驅動的,事件都有觸發條件。
在WebForm或者WinForm中,我們經常看到:
private void button1_Click(object sender, EventArgs e) { //代碼 }
上面是一個按鈕的單擊事件。從上可以看到三個事件因素:
我們在Winform中都是通過如下的方式來注冊事件的。
this.button1.Click += new System.EventHandler(this.button1_Click);
EventHandler就是一個委托:
public delegate void EventHandler(object sender, EventArgs e);
這也就是為什麼我們注冊的事件總是有sender和e這兩個參數,因為委托就是這樣聲明的。我們來自定義一個事件:
public event EventHandler OnSay; public Form1() { InitializeComponent(); OnSay += Form1_OnSay; } void Form1_OnSay(object sender, EventArgs e) { Console.Write("你好嗎"); }
我們通過Reflector工具來查看:
事件OnSay中,其實是2個方法,我們來看下源碼:
public void add_OnSay(EventHandler value) { EventHandler handler2; EventHandler onSay = this.OnSay; do { handler2 = onSay; EventHandler handler3 = (EventHandler)Delegate.Combine(handler2, value); onSay = Interlocked.CompareExchange<EventHandler>(ref this.OnSay, handler3, handler2); } while (onSay != handler2); } public void remove_OnSay(EventHandler value) { EventHandler handler2; EventHandler onSay = this.OnSay; do { handler2 = onSay; EventHandler handler3 = (EventHandler)Delegate.Remove(handler2, value); onSay = Interlocked.CompareExchange<EventHandler>(ref this.OnSay, handler3, handler2); } while (onSay != handler2); }
這裡可以看出對事件的操作,其實最終還是體現在對委托的操作。
為什麼要有泛型?
更好的實現代碼復用,但是它不是通過面向對象的思想來實現代碼復用。面向對象慣用的三板斧:封裝、繼承、多態。
我們先來看一下代碼,假設在一個類中有多個方法,他們的操作很類似,可能僅僅只是傳入的參數類型不同而已
using System; namespace GenericsDemo { public class MethodTest { public void IntShow(int i) { Console.WriteLine(string.Format("IntShow方法,參數類型{0}",i.GetType())); } public void StrShow(string s) { Console.WriteLine(string.Format("StrShow方法,參數類型{0}", s.GetType())); } } }
如果一個類中存在多個這樣的方法,我們總不能把所有的方法都這麼寫一遍吧,有沒有一種方式來將這些方法進行合並呢?
這個時候我們會想到Object是任何類型的父類,任何父類出現的地方,都可以使用子類來代替。接下來,我們來改造一下代碼實現:
public void ObjShow(object obj) { Console.WriteLine(string.Format("ObjShow方法,參數類型{0}", obj.GetType())); }
我們來看下調用:
_MethodTest.IntShow(1); _MethodTest.StrShow("1"); _MethodTest.ObjShow(1); _MethodTest.ObjShow("1");
方法是合並了,但是現在存在什麼樣的問題?出現了裝箱拆箱,嚴重影響性能。而且不夠安全,因為如果當我把代碼進行如下修改時,會發生什麼
public void ObjShow(object obj) { //Console.WriteLine(string.Format("ObjShow方法,參數類型{0}", obj.GetType())); Console.WriteLine(string.Format("ObjShow方法,參數類型{0},參數值{1}", obj.GetType(),Convert.ToInt32(obj))); }
調用代碼:_MethodTest.ObjShow("a");
編譯時不會報錯,但是運行時就報錯了。也就是說通過object來作為參數傳遞,其實是存在嚴重的安全隱患的。
那麼有沒有什麼辦法來解決這兩個問題呢?C#2.0泛型的出現正是基於這樣的需求。
public void GenericsShow<T>(T t) { Console.WriteLine(string.Format("GenericsShow方法,參數類型{0}", t.GetType())); }
調用代碼:_MethodTest.GenericsShow<int>(1);
這樣依賴,泛型方法在申明的時候能夠實現類似於Objet的效果,在調用時先確定類型,這樣就達到了安全檢查的目的。
泛型就像是使用了一個類型占位符,而這一特性在使用集合時更能體現其強大之處。
也正是由於泛型太強大了,強大得像孫悟空一樣,我們需要弄一道緊箍咒來對其進行束縛,否則不容易控制。這時,就有了泛型約束,它在泛型方法或者泛型委托聲明之時就對其進行限定。限定關鍵字通過where。
public class Student { public string Name { get; set; } } public void StudentShow<T>(T t) where T : Student { Console.WriteLine(string.Format("GenericsShow方法,參數類型{0}", t.GetType())); } public void GenericsShow<T>(T t) { Console.WriteLine(string.Format("GenericsShow方法,參數類型{0}", t.GetType())); } public void GenericsShow<T>(T t) { Console.WriteLine(string.Format("GenericsShow方法2,參數類型{0}", t.GetType())); }
需要注意的是,這裡使用了泛型重載,這個時候編譯是可以正常通過的,可是注意了,調用的時候就出現問題了
為什麼會這樣呢?因為泛型的類型參數在編譯器並不能確定其類型,而重載時進行類型檢查發送在實例方法被調用時。
同時需要注意的是,當一般方法和泛型方法同時調用時,優先選擇一般方法,因為編譯器會進行類型推斷。
泛型的運用遠不止於此,它還支持泛型繼承、泛型接口、泛型類、泛型委托等。