C#中的委托數(shù)據(jù)類型簡(jiǎn)介
什么是委托?
委托是一個(gè)類型安全的對(duì)象,它指向程序中另一個(gè)以后會(huì)被調(diào)用的方法(或多個(gè)方法)。通俗的說,委托是一個(gè)可以引用方法的對(duì)象,當(dāng)創(chuàng)建一個(gè)委托,也就創(chuàng)建一個(gè)引用方法的對(duì)象,進(jìn)而就可以調(diào)用那個(gè)方法,即委托可以調(diào)用它所指的方法。
來看下面的例子,類deleMthod定義了3個(gè)方法,add、minus和multi,他們都具有相同的輸入?yún)?shù)列表(int x,int y)和輸出參數(shù)類型int,那么我們就說這三個(gè)方法具有相同的方法簽名。開發(fā)者可以抽象地用 int 某名稱(int x,int y) 的一種類型對(duì)方法進(jìn)行封裝,在c#中這種抽象的數(shù)據(jù)類型叫委托,針對(duì)上述的幾個(gè)方法我們可以定義委托 : public delegate int Handler(int x ,int y),public 是一個(gè)訪問修飾符,delegate關(guān)鍵字表示這是一個(gè)委托,int Hander(int x,int y)表示這個(gè)委托的名稱。
class deleMethod { public int add(int x, int y) { return x + y; } public int minus(int x, int y) { return x - y; } public int multi(int x, int y) { return x * y; } }
怎么使用委托
使用委托大體有四個(gè)步驟:
•定義一個(gè)委托,上節(jié)已經(jīng)提及。
•定義委托方法,上節(jié)deleMethod類中add、minus、multi都是委托方法,定義的目的就是為了使用它,講專業(yè)點(diǎn)就是為了方法的調(diào)用
•委托變量及賦值,和類型一樣,在使用前需要對(duì)變量賦值。
•委托變量的使用。
怎樣定義委托變量,還是接著上面的例子。我們已經(jīng)定義了一個(gè)委托類型 public delegate int Handler(int x,int y),和c#語(yǔ)法規(guī)范一樣定義一個(gè)變量并賦值語(yǔ)法是:“類型名 變量名 = new 類型名(方法);”,如上例
“Handler deleCall = new Handler(方法名);“,在.net2.0后對(duì)于委托的實(shí)例化可以簡(jiǎn)化為” Handler deleCall = 方法名;“。
委托變量的使用,也就是對(duì)委托方法的調(diào)用。其語(yǔ)法是”int result1 = deleCall(10,20);“或者使用委托調(diào)用方法 Invoke,“int result2 = deleCall.Invoke(10,20);”。
具體如何使用可以看看下面的示例:
class Program { public delegate int Handler(int x, int y); //---定義委托的類型,可以將委托看成一種特殊的數(shù)據(jù)類型 static void Main(string[] args) { deleMethod dm = new deleMethod(); //---實(shí)例化包含委托方法的類型 Handler deleCall = new Handler(dm.add); //---定義委托變量delCall,并出示化賦值 int result1 = deleCall(10, 20); //---實(shí)例方法的調(diào)用invoke Console.WriteLine("the add resutl is:{0}", result1); deleCall = dm.minus; int result2 = deleCall.Invoke(12, 6); Console.WriteLine("the minus result is:{0}", result2); Console.ReadLine(); } }
如上例所示,定義一個(gè)簡(jiǎn)單的加、減功能如此的復(fù)雜,攪來攪去讓人頭,真是無語(yǔ),難怪很多朋友談委托色變暈。在實(shí)際使用的過程中,c#還是有很多方式幫我們簡(jiǎn)化代碼。
簡(jiǎn)化委托
預(yù)定義的泛型委托
c#系統(tǒng)最常見的預(yù)定義的委托類型有三種,F(xiàn)unc<>委托、Action<>委托、Predicate<>委托,F(xiàn)unc<>委托是一個(gè)有返回值的委托,輸入?yún)?shù)可以多達(dá)16個(gè);而Action<>委托是一個(gè)沒有返回值的委托,它的輸入?yún)?shù)也可以多達(dá)16個(gè);而Predicate<>是一個(gè)具有bool返回類型的委托,它只運(yùn)行一個(gè)輸入?yún)?shù)。對(duì)于有上例的委托類型,我們可以使用預(yù)定義的委托類型Fun<int,int,int>來代替,省去我們自己定義一個(gè)什么鬼東西 public delegate int Handler(int x,int y)類型,其代碼其實(shí)可以簡(jiǎn)化為如下例所示:
namespace DelegateDemo1 { class Program { static void Main(string[] args) { deleMethod dm = new deleMethod(); Func<int, int, int> fun = dm.add; //---使用預(yù)定義的委托類型Func<> int result4 = fun(8, 10); Func<int, int, int> fun1 = dm.minus; int result5 = fun1(12, 8); Console.WriteLine("預(yù)定義的委托輸出{0},{1}", result4, result5); Console.ReadLine(); } } class deleMethod { public int add(int x, int y) { return x + y; } public int minus(int x, int y) { return x - y; } public int multi(int x, int y) { return x * y; } } }
我把委托的方法定義和委托的調(diào)用放在一起看,是不是比原先自己定義的一個(gè)委托類型簡(jiǎn)單方便一些?但是這樣使用委托還是不怎么清爽,估計(jì)在實(shí)際應(yīng)用中很少人會(huì)怎么寫代碼,太不方便了。
匿名委托
當(dāng)委托實(shí)例的調(diào)用和委托方法的定義分開處理,碼農(nóng)們?cè)谧x程序代碼的時(shí)候需要來回的去找委托方法去匹配委托變量,看看參數(shù)列表和返回值是否正確,這樣的程序代碼的可讀性很差。其實(shí)c#還是有方法讓我們簡(jiǎn)化代碼:那就是匿名委托,將方法體直接在委托的實(shí)例化時(shí)給出,之所以叫匿名委托就是再定義委托的時(shí)候省略掉委托的名稱,它的定義語(yǔ)法是delegate(參數(shù)1,參數(shù)2) 后面直接就給出方法體,用大括號(hào)將方法體括起。剛看起來比較怪異,接觸多了也就習(xí)慣了,莫有辦法只能去適應(yīng)c#的語(yǔ)法規(guī)范。話說多了是水,還不如看代碼來得直接。
namespace DelegateDemo1 { class Program { static void Main(string[] args) { Func<int, int, int> fun = delegate(int x, int y) { return x + y; }; Func<int, int, int> fun1 = delegate(int x, int y) { return x - y; }; int result4 = fun(8, 10); int result5 = fun1(12, 8); Console.WriteLine("預(yù)定義的委托輸出{0},{1}", result4, result5); Console.ReadLine(); } } }
看看是不是在原來的基礎(chǔ)上大幅度減少了代碼量,腫么辦,是否代碼量已經(jīng)減少到極致了?
lambda表達(dá)式
其實(shí)對(duì)于委托的定義還可以進(jìn)一步簡(jiǎn)化,那就是使用lambda表達(dá)式,lambda表達(dá)式的定義是(參數(shù)列表)=>{方法體},=>讀作goes to。lambda表達(dá)式對(duì)參數(shù)列表和方法表達(dá)式的精簡(jiǎn)達(dá)到極致,對(duì)于上面的例子,用λ表達(dá)式可以省略掉匿名委托的關(guān)鍵字和參數(shù)類型,系統(tǒng)可以進(jìn)行類型推斷,不影響運(yùn)行,其簡(jiǎn)化的代碼如下。對(duì).net 1.0,2.0最傳統(tǒng)的委托定義和使用,是一個(gè)巨大的簡(jiǎn)化,它剔除了所有多余的語(yǔ)句達(dá)到極致。
namespace DelegateDemo1 { class Program { static void Main(string[] args) { Func<int, int, int> fun = (x, y) => x + y; Func<int, int, int> fun1 = (x, y) => x - y; int result4 = fun(8, 10); int result5 = fun1(12, 8); Console.WriteLine("預(yù)定義的委托輸出{0},{1}", result4, result5); Console.ReadLine(); } } }
委托鏈
前面講過,委托在本質(zhì)上仍然是一個(gè)類,我們用delegate關(guān)鍵字聲明的所有委托都繼承自System.MulticastDelegate。后者又是繼承自System.Delegate類,System.Delegate類則繼承自System.Object。一個(gè)委托可以綁定若干相同簽名的方法形成一個(gè)委托鏈,委托鏈?zhǔn)且粋€(gè)委托實(shí)例的集合,它允許我們調(diào)用這個(gè)集合中的委托實(shí)例所代表的所有方法(對(duì)于有返回值的方法,委托鏈的返回值為鏈表中最后一個(gè)方法的返回值),在下面的例子我們定義的委托方法都沒有返回值。我們可以用 GetInvocationList()方法獲取委托鏈。
class Program { static void Main(string[] args) { //Action 表示沒有返回值的一類方法 Action<int, int> actionA = (x, y) => { Console.WriteLine("x是{0},y是{1},他們的平方和是{2}", x, y, x * x + y * y); }; actionA += (x, y) => { Console.WriteLine("x是{0},y是{1},他們的平方差是{2}", x, y, x * x - y * y); }; actionA(10, 5); foreach (var item in actionA.GetInvocationList()) Console.WriteLine(item.Method); Console.ReadLine(); } }
什么是事件
經(jīng)常看到一種定義是:事件是一種特殊的委托,是對(duì)委托的封裝。其實(shí)這種定義是很不嚴(yán)謹(jǐn)?shù)摹N惺且环N數(shù)據(jù)類型,但是事件只是委托的實(shí)例,不能算是一種數(shù)據(jù)類型,所以說事件是一種特殊的委托是不準(zhǔn)確的。如果這樣定義:事件是一種特殊的委托實(shí)例,是對(duì)委托的封裝。那么在C#中事件是如何定義并被使用的呢?其實(shí)事件從定義到使用要經(jīng)過四個(gè)階段。
•定義事件依賴的委托,并定義事件以及引發(fā)事件的方法,該步驟可以定義在事件發(fā)布器類中
•定義事件所依賴的事件方法,該步驟可以定義在事件訂閱者類中
•如果有事件參數(shù),該步驟可以定義在事件參數(shù)類中
•注冊(cè)事件并引發(fā)事件,該步驟一般寫在主程序中
一般來講在事件定義過程中,各個(gè)方法的命名沒有統(tǒng)一的標(biāo)準(zhǔn),但我們可以參考微軟在c#中的命名規(guī)范進(jìn)行命名
建議事件委托命名為:事件+EventHandler。如果有額外的參數(shù)傳遞需定義自己的參數(shù)類型,參數(shù)類型的命名規(guī)范 :事件+EventHandler。比如,可以定義一個(gè)事件委托 public delegate void calcEventHandler(object sender,calcEventArgs e); 。
定義一個(gè)事件變量如:public event calcEventHandler calc;
定義一個(gè)引發(fā)事件的方法如:public void onCalc(object sender,calcEventArgs e){}
建議方法名為 on+事件,就如同我們?cè)陂_發(fā)web程序時(shí),綁定的事件名有onClick,onLoad等一樣。
參考看下面的例子,了解定義一個(gè)事件的一般過程,如果不需要傳遞事件的參數(shù)可以省去事件參數(shù)類的定義,使用系統(tǒng)預(yù)定義的EventArgs就可以了。
/// <summary> /// 主程序類 /// </summary> class Program { static void Main(string[] args) { eventPublish myEvent = new eventPublish(); eventSubscription myMethod = new eventSubscription(); //綁定方法 add 和 subtract,又稱為對(duì)事件方法的注冊(cè) -=稱為對(duì)事件方法的注銷 myEvent.calc += myMethod.add; myEvent.calc += myMethod.substract; while (true) { try { Console.WriteLine("請(qǐng)輸入第一個(gè)整數(shù)數(shù)"); int numA = Convert.ToInt16(Console.ReadLine()); Console.WriteLine("請(qǐng)輸入第二個(gè)整數(shù)"); int numB = Convert.ToInt16(Console.ReadLine()); calcEventArgs e = new calcEventArgs(numA, numB); //在本例不需要sender參數(shù),隨意傳遞了字符串"sender" myEvent.onCalc("sender", e); } catch (Exception ex) { Console.WriteLine("出現(xiàn)錯(cuò)誤," + ex.Message); } } } } /// <summary> /// 定義一個(gè)事件發(fā)布器類 /// </summary> class eventPublish { //定義一個(gè)委托類型,委托名一般是 事件變量名+EventHandler public delegate void calcEventHander(object sender,calcEventArgs e); //定義一個(gè)事件變量,其變量名為calc public event calcEventHander calc; //封裝事件,對(duì)外定義了引發(fā)事件的方法,定義的引發(fā)方法名一般是 on+事件變量名 public void onCalc(object sender, calcEventArgs e) { if (calc != null) calc(sender,e); } } /// <summary> /// 定義一個(gè)事件訂閱者類(事件方法類) /// </summary> class eventSubscription { public void add(object sender, calcEventArgs e) { Console.WriteLine("兩數(shù)相加等于{0}", e.X + e.Y ); } public void substract(object sender, calcEventArgs e) { Console.WriteLine("兩數(shù)相減等于{0}", e.X - e.Y); } } /// <summary> /// 定義一個(gè)事件參數(shù)類 /// </summary> class calcEventArgs : EventArgs { private int _x; private int _y; public calcEventArgs(int x, int y) { this._x = x; this._y = y; } public int X { get { return _x; } } public int Y { get { return _y; } } }
我們將事件是對(duì)委托的封裝,如果用ILDAS反編譯可執(zhí)行文件查看中間代碼就可以非常明了的看出事件運(yùn)行機(jī)制
如上圖所示,事件calc,經(jīng).net運(yùn)行時(shí)編譯為中間語(yǔ)言后產(chǎn)生了一個(gè)private 的calc屬性,同時(shí)也自動(dòng)生成了add_calc和remove_calc方法,用于注冊(cè)和注銷訂閱者方法。因?yàn)槭录潜环庋b的,盡管calc屬性是private,但在所以在發(fā)布器類內(nèi)部可以用 calc(sender,e)這樣的方法直接調(diào)用;但在主程序中如果這樣使用就會(huì)出錯(cuò),只能通過onCalc方法進(jìn)行間接調(diào)用。
后記
本篇文章,一開始提出了什么是委托的疑問,通過引入幾個(gè)方法來講述委托是什么以加強(qiáng)對(duì)委托概念的理解。第二部分講述了使用委托的四個(gè)步驟,并通過示例闡述了這幾個(gè)步驟。第三部分講述了委托使用的簡(jiǎn)化問題,通過使用泛型委托簡(jiǎn)化自定義委托,通過使用匿名委托可以簡(jiǎn)化定義委托方法。匿名委托是在定義委托的時(shí)候直接給出方法體,通過使用lambda表達(dá)式的類型推斷可進(jìn)一步簡(jiǎn)化委托的使用。第四部分講述了委托鏈,通過綁定方法初始化委托,并通過+=綁定更多的委托方法。第五部分講述了事件的定義和使用的四個(gè)步驟。當(dāng)然委托的使用場(chǎng)景還有很多,比如通過BeginInvoke和EndInvoke進(jìn)行異步調(diào)用,因不是本篇文章的重點(diǎn),所以文章中沒有提及。
以上內(nèi)容是小編給大家介紹的C#中的委托數(shù)據(jù)類型簡(jiǎn)介,希望對(duì)大家有所幫助!
上一篇:C#使用LINQ查詢表達(dá)式的基本子句總結(jié)
欄 目:C#教程
下一篇:C#在WinForm中使用WebKit傳遞js對(duì)象實(shí)現(xiàn)與網(wǎng)頁(yè)交互的方法
本文標(biāo)題:C#中的委托數(shù)據(jù)類型簡(jiǎn)介
本文地址:http://mengdiqiu.com.cn/a1/C_jiaocheng/6639.html
您可能感興趣的文章
- 01-10Extjs4如何處理后臺(tái)json數(shù)據(jù)中日期和時(shí)間
- 01-10C#異步綁定數(shù)據(jù)實(shí)現(xiàn)方法
- 01-10C#中查找Dictionary中的重復(fù)值的方法
- 01-10C#動(dòng)態(tài)創(chuàng)建Access數(shù)據(jù)庫(kù)及密碼的方法
- 01-10C#使用ADO.Net部件來訪問Access數(shù)據(jù)庫(kù)的方法
- 01-10C#實(shí)現(xiàn)ComboBox控件顯示出多個(gè)數(shù)據(jù)源屬性的方法
- 01-10C#將圖片存放到SQL SERVER數(shù)據(jù)庫(kù)中的方法
- 01-10關(guān)于nancy中的身份驗(yàn)證
- 01-10C#中的事務(wù)用法實(shí)例分析
- 01-10C#編程自學(xué)之?dāng)?shù)據(jù)類型和變量二


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 01-10C#通過反射獲取當(dāng)前工程中所有窗體并
- 01-10關(guān)于ASP網(wǎng)頁(yè)無法打開的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#實(shí)現(xiàn)txt定位指定行完整實(shí)例
- 01-10WinForm實(shí)現(xiàn)仿視頻播放器左下角滾動(dòng)新
- 01-10C#停止線程的方法
- 01-10C#實(shí)現(xiàn)清空回收站的方法
- 01-10C#通過重寫Panel改變邊框顏色與寬度的
- 01-10C#實(shí)現(xiàn)讀取注冊(cè)表監(jiān)控當(dāng)前操作系統(tǒng)已
隨機(jī)閱讀
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 01-10C#中split用法實(shí)例總結(jié)
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 01-10delphi制作wav文件的方法
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 04-02jquery與jsp,用jquery