詳解C#中delegate/event/EventHandler/Action/Func的使用和區(qū)別
1 委托
在.NET中定義“委托”需要用到delegate關(guān)鍵字,它是存有對(duì)某個(gè)方法的引用的一種引用類型變量,類似于 C 或 C++ 中函數(shù)的指針。“委托”主要有兩大作用:
(1)將方法當(dāng)作參數(shù)傳遞
(2)方法的一種多態(tài)(類似于一個(gè)方法模板,可以匹配很多個(gè)方法)
下面,給出一個(gè)展現(xiàn)了上述兩大作用的委托代碼示例:
//定義一個(gè)委托 public delegate int MyDelegate(int x, int y); //與委托匹配的一個(gè)方法 public static int Add(int a, int b) { return a + b; } //與委托匹配的另一個(gè)方法 public static int Reduce(int a, int b) { return a - b; } //示例:將委托/方法當(dāng)參數(shù)傳遞 public static int Test(MyDelegate MD) { return MD(10, 20); } static void Main(string[] args) { int a, b, x, y; MyDelegate md; //將委托指向Add這個(gè)方法,并進(jìn)行相關(guān)操作 md = Add; a = md(1, 2); b = Test(md); //再將委托指向Reduce這個(gè)方法,并進(jìn)行相關(guān)操作 md = Reduce; x = md(7, 2); y = Test(md); Console.WriteLine($"1+2={a},10+20=,7-2={x},10-20={y}"); Console.ReadLine(); }
執(zhí)行以上程序,輸出結(jié)果如下:
1+2=3,10+20=30,7-2=5,10-20=-10
委托也可以使用+=/-=來實(shí)現(xiàn)“發(fā)布/訂閱”模式,示例代碼如下:
//定義一個(gè)委托 public delegate void MyDelegate1(int x); public static void Method1(int a) { Console.WriteLine($"a={a}"); } public static void Method2(int b) { Console.WriteLine($"b="); } static void Main(string[] args) { MyDelegate1 md = null; md += Method1; md += Method2; md(35); Console.ReadLine(); }
以上程序輸出如下:
a=35
b=35
但是委托有一個(gè)弊端,它可以使用“=”將所有已經(jīng)訂閱的取消,只保留=后的這一個(gè)訂閱。
為了解決這個(gè)弊端,事件event應(yīng)運(yùn)而生。
2 事件-概念的引出
事件event是一種特殊的委托,它只能+=,-=,不能直接用=。
event在定義類中(發(fā)布者)是可以直接=的,但是在其他類中(訂閱者)就只能+= -=了,也就是說發(fā)布者發(fā)布一個(gè)事件后,訂閱者針對(duì)他只能進(jìn)行自身的訂閱和取消。
下面是定義一個(gè)事件的代碼:
//定義一個(gè)委托 public delegate void MyDelegate1(int x); //定義一個(gè)事件 public event MyDelegate1 emd;
經(jīng)過長久的經(jīng)驗(yàn)積累后,人們發(fā)現(xiàn),絕大多數(shù)事件的定義,是用public delegate void XXX(object sender, EventArgs e);這樣一個(gè)委托原型進(jìn)行定義的,是一件重復(fù)性的工作,于是,EventHandler應(yīng)運(yùn)而生。它的出現(xiàn)就是為了避免這種重復(fù)性工作,并建議盡量使用該類型作為事件的原型。
//@sender: 引發(fā)事件的對(duì)象 //@e: 傳遞的參數(shù) public delegate void EventHandler(object sender, EventArgs e); //使用 public event EventHandler emd;
下面給出一個(gè)使用事件的具體示例:
public class Demo { public event EventHandler emd; public void RaiseEvent() { emd(this, EventArgs.Empty); } } static void Main(string[] args) { var instance = new Demo(); instance.emd += (sender, arg) => { Console.WriteLine("執(zhí)行事件1!"); }; instance.emd += (sender, arg) => { Console.WriteLine("執(zhí)行事件2!"); }; instance.RaiseEvent(); Console.ReadLine(); }
這里我們先定義一個(gè)Demo類,其內(nèi)部有個(gè)事件是emd,我們給他開放了一個(gè)接口RaiseEvent,如果誰敢調(diào)用它,那么,它就觸發(fā)報(bào)警事件emd。
這里模擬了2個(gè)訂閱者,分別處理報(bào)警事件emd。
程序執(zhí)行結(jié)果如下:
執(zhí)行事件1!
執(zhí)行事件2!
同時(shí),我們也可以看出:事件是按照+=的訂閱先后順序執(zhí)行的。
3 事件-關(guān)于異常
現(xiàn)在,我們?cè)诘谝粋€(gè)訂閱者中加入異常,如下:
instance.emd += (sender, arg) => { Console.WriteLine("執(zhí)行事件1!"); throw new Exception("執(zhí)行事件1,錯(cuò)誤"); };
執(zhí)行后發(fā)現(xiàn),第1個(gè)訂閱者事件觸發(fā)拋出異常后,第2個(gè)訂閱者的事件沒有執(zhí)行。
可見,如果你想讓所有訂閱者都執(zhí)行處理的話,那每個(gè)訂閱者必須在訂閱程序內(nèi)自己處理好異常,不能拋出來!
4 事件-關(guān)于異步
如果事件的訂閱者中有一個(gè)是“異步”處理,又會(huì)是什么情況?
下面我們把第1個(gè)訂閱者改為異步處理,代碼如下:
instance.emd += async (sender, arg) => { Console.WriteLine("執(zhí)行事件1!"); await Task.Delay(1000); Console.WriteLine("執(zhí)行事件1!完畢"); };
執(zhí)行后輸出如下:
執(zhí)行事件1!
執(zhí)行事件2!
執(zhí)行事件1!完畢
可見,異步的事件處理沒有阻塞進(jìn)程,很好的起到了異步方法的作用。
5 委托-Func與Action
本文最開始探討委托,然后直接順到了事件的相關(guān)話題上。其實(shí),關(guān)于委托還有一個(gè)重點(diǎn)話題漏掉了,那就是Func與Action。
在委托delegate出現(xiàn)了很久以后,微軟的.NET設(shè)計(jì)者們終于領(lǐng)悟到,其實(shí)所有的委托定義都可以歸納并簡化成只用Func與Action這兩個(gè)語法糖來表示。其中,F(xiàn)unc代表有返回值的委托,Action代表無返回值的委托。有了它們兩,我們以后就不再需要用關(guān)鍵字delegate來定義委托了。
同時(shí),若再用lambda表達(dá)式取代被委托指向的具體方法,則整個(gè)委托的“定義+賦值”兩步將大大簡化(lambda表達(dá)式本來也是方法定義的一種簡化形式)。
下面,把最開始委托章節(jié)中關(guān)于加減法的程序代碼,用Func與lambda表達(dá)式進(jìn)行簡化改造,改造后的代碼如下:
//示例:將委托/方法當(dāng)參數(shù)傳遞 public static int Test(Func<int, int, int> MD) { return MD(10, 20); } static void Main(string[] args) { int a, b, x, y; Func<int, int, int> md; //將委托指向加法,并進(jìn)行相關(guān)操作 md = (t, v) => t + v; a = md(1, 2); b = Test(md); //再將委托指向減法,并進(jìn)行相關(guān)操作 md = (t, v) => t - v; x = md(7, 2); y = Test(md); Console.WriteLine($"1+2={a},10+20=,7-2={x},10-20={y}"); Console.ReadLine(); }
是不是代碼大大簡化了?簡化了哪些內(nèi)容,你可以前后對(duì)比一下...
以上就是詳解C#中delegate/event/EventHandler/Action/Func的使用和區(qū)別的詳細(xì)內(nèi)容,更多關(guān)于C# delegate event EventHandler Action Func的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解ASP.NET中Identity的身份驗(yàn)證代碼
這篇文章主要介紹了ASP.NET Identity 的“多重”身份驗(yàn)證代碼,以及實(shí)現(xiàn)的原理講解,需要的朋友參考一下。2017-12-12datatable生成excel和excel插入圖片示例詳解
excel導(dǎo)出在C#代碼中應(yīng)用己經(jīng)很廣泛了,下面講了datatable生成excel、復(fù)制sheet頁、刪除sheet頁、選中sheet頁、另存excel文件、excel中插入圖片等功能2014-01-01C#中判斷、驗(yàn)證字符串是否為日期格式的實(shí)現(xiàn)代碼
這篇文章主要介紹了C#中判斷、驗(yàn)證字符串是否為日期格式的實(shí)現(xiàn)代碼,使用DateTime類中自帶的兩個(gè)方法實(shí)現(xiàn),需要的朋友可以參考下2014-08-08C#中GridView動(dòng)態(tài)添加列的實(shí)現(xiàn)方法
這篇文章主要介紹了C#中GridView動(dòng)態(tài)添加列的實(shí)現(xiàn)方法,涉及C#中GridView的相關(guān)使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07