詳解C#中的委托
委托這個(gè)東西不是很好理解,可是工作中又經(jīng)常用到,你隨處可以看到它的身影,真讓人有一種又愛又恨的感覺,我相信許多人被它所困擾過。
一提到委托,如果你學(xué)過C語言,你一定會(huì)馬上聯(lián)想到函數(shù)指針。
什么是委托?委托是C#中類型安全的,可以訂閱一個(gè)或多個(gè)具有相同簽名方法的函數(shù)指針。委托可以把函數(shù)做為參數(shù)傳遞,其實(shí)際意義便是讓別人代理你的事情。委托可以看做是函數(shù)的指針,整數(shù)可以用整數(shù)變量指向它,對(duì)象可以用對(duì)象變量指向它,
函數(shù)也可以用委托變量指向它。我們可以選擇將委托類型看做只定義了一個(gè)方法的接口,而委托的實(shí)例可以看做是實(shí)現(xiàn)了那個(gè)接口的一個(gè)對(duì)象。
使用委托,必須滿足4個(gè)條件:
- 聲明委托類型;
- 必須有一個(gè)方法包含了要執(zhí)行的代碼;
- 必須創(chuàng)建一個(gè)委托實(shí)例;
- 必須調(diào)用(invoke)委托實(shí)例。
委托的申明
聲明委托的方式:delegate 返回值類型 委托類型名(參數(shù))
委托的申明和接口方法的申明基本上一致,只是在返回類型關(guān)鍵字的前面多了一個(gè)delegate關(guān)鍵字。還有就是委托一般聲明為public類型,因?yàn)樗S時(shí)要供別人調(diào)用的。
委托的本質(zhì)也是一個(gè)類型。我們聲明一個(gè)類可以進(jìn)行實(shí)例化,同樣委托也可以進(jìn)行實(shí)例化。
有如下四種委托:
//1.無參數(shù)無返回值
public delegate void NoParaNoReturnEventHandler();
//2.有參數(shù)無返回值
public delegate void WithParaNoReturnEventHandler(string name);
//3.無參數(shù)有返回值
public delegate string NoParaWithReturnEventHandler();
//4.有參數(shù)有返回值
public delegate string WithParaWithReturnEventHandler(string name);
如果代碼想要執(zhí)行操作,但不知道操作細(xì)節(jié),一般可以使用委托。例如, Thread類之所以知道要在一個(gè)新線程里運(yùn)行什么,唯一的原因就是在啟動(dòng)新線程時(shí),向它提供了一個(gè)ThreadStart或ParameterizedThreadStart委托實(shí)例。
Thread th = new Thread(Test); th.Start(); public Thread(ThreadStart start); public delegate void ThreadStart();
ThreadStart是一個(gè)無參無返回值的委托。
static void Test()
{
Console.WriteLine("線程方法");
}
這個(gè)Test方法的函數(shù)簽名必須和委托ThreadStart的函數(shù)簽名一致。
委托的調(diào)用
必須先實(shí)例化委托,然后再調(diào)用。
函數(shù)的簽名和委托的簽名必須一致。NoParaNoReturnEventHandler _NoParaNoReturnEventHandler = ConsoleInfo;,編譯器幫我們進(jìn)行了new,但是不能寫成NoParaNoReturnEventHandler _NoParaNoReturnEventHandler = ConsoleInfo();
因?yàn)檫@樣就成為了函數(shù)調(diào)用。
#region 無返回值委托調(diào)用
public static void Show()
{
//實(shí)例化委托
NoParaNoReturnEventHandler _NoParaNoReturnEventHandler = new NoParaNoReturnEventHandler(ConsoleInfo);
//NoParaNoReturnEventHandler _NoParaNoReturnEventHandler = ConsoleInfo; //簡(jiǎn)寫
//委托調(diào)用 通過Invoke()調(diào)用,或者可以直接省略
_NoParaNoReturnEventHandler.Invoke();
//_NoParaNoReturnEventHandler();
}
private static void ConsoleInfo()
{
Console.WriteLine("無參數(shù)無返回值的函數(shù)調(diào)用");
}
#endregion
沒有委托就沒有異步,異步正是因?yàn)槲械拇嬖凇?/p>
_NoParaNoReturnEventHandler.BeginInvoke(null,null); //異步調(diào)用
為什么要使用委托
我們完全可以直接調(diào)用方法,為什么還需要通過一個(gè)委托來調(diào)用呢?委托有什么意義?
解耦,對(duì)修改關(guān)閉,對(duì)擴(kuò)展開放。邏輯分離。
你可以把委托理解為函數(shù)的父類,或者是一個(gè)方法的占位符。
我們來看下代碼,假設(shè)有2個(gè)方法,一個(gè)說英語,一個(gè)說漢語,而這2個(gè)方法的函數(shù)簽名是一樣的。
public static void SayChinese(string name)
{
Console.WriteLine("你好," + name);
}
public static void SayEnglish(string name)
{
Console.WriteLine("hello," + name);
}
那么我們?cè)谕獠空{(diào)用的時(shí)候,
MyDelegate.SayChinese("張三");
MyDelegate.SayEnglish("zhangsan");
如果要調(diào)用這兩個(gè)不同的方法,是不是要寫不同的調(diào)用代碼
我們能不能只一個(gè)方法調(diào)用呢?修改代碼如下:
public static void Say(string name,WithParaNoReturnEventHandler handler)
{
handler(name);
}
public static void SayChinese(string name)
{
Console.WriteLine("你好," + name);
}
public static void SayEnglish(string name)
{
Console.WriteLine("hello," + name);
}
這樣,只通過一個(gè)方法Say來進(jìn)行調(diào)用。
如何調(diào)用呢?如下三種調(diào)用方式:
WithParaNoReturnEventHandler _WithParaNoReturnEventHandler = new WithParaNoReturnEventHandler(MyDelegate.SayChinese);
MyDelegate.Say("張三",_WithParaNoReturnEventHandler);
MyDelegate.Say("張三", delegate(string name) { Console.WriteLine("你好," + name); }); //匿名方法
MyDelegate.Say("張三", (name) => { Console.WriteLine("你好," + name); }); //lambda表達(dá)式
以上代碼使用了幾種調(diào)用方式,這些調(diào)用方式都是隨著C#的升級(jí)而不斷優(yōu)化的。第一種是C#1.0中就存在的傳統(tǒng)調(diào)用方式,第二種是C#2.0中的匿名方法調(diào)用方式,所謂匿名方法,就是沒有名字的方法,當(dāng)方法只調(diào)用一次時(shí)使用匿名方法最合適不過了。C#3中的lambda表達(dá)式。其實(shí)泛型委托同樣是被支持的,而.NET 3.5則更進(jìn)一步,引入了一組名為Func的泛型委托類型,它能獲取多個(gè)指定類型的參數(shù),并返回另一個(gè)指定類型的值。
lambda表達(dá)式
lambda表達(dá)式的本質(zhì)就是一個(gè)方法,一個(gè)匿名方法。
如果方法體只有一行,無返回值,還可以去掉大括號(hào)和分號(hào)。
MyDelegate.Say("張三", (name) => Console.WriteLine("你好," + name));
如果方法體只有一行,有返回值,可以去掉大括號(hào)和return。
WithParaWithReturnEventHandler _WithParaWithReturnEventHandler = (name)=>name+",你好";
從.NET3.5開始,基本上不需要我們自己來申明委托了,因?yàn)橄到y(tǒng)有許多內(nèi)置的委托。
Action和Func委托,分別有16個(gè)和17個(gè)重載。int表示輸入?yún)?shù),out代表返回值,out參數(shù)放置在最后。
Action表示無返回值的委托,F(xiàn)unc表示有返回值的委托。因?yàn)榉椒◤拇蟮慕嵌葋矸诸?,也分為有返回值的方法和無返回值的方法。
也就是說具體調(diào)用什么樣的方法,完全由調(diào)用方?jīng)Q定了,就有了更大的靈活性和擴(kuò)展性。為什么這么說,如果我有些時(shí)候要先說英語再說漢語,有些事時(shí)候要先說漢語再說英語,如果沒有委托,我們會(huì)怎么樣實(shí)現(xiàn)?請(qǐng)看如下代碼:
public static void SayEnglishAndChinese(string name)
{
SayEnglish(name);
SayChinese(name);
}
public static void SayChineseAndEnglish(string name)
{
SayChinese(name);
SayEnglish(name);
}
如果又突然要添加一種俄語呢?被調(diào)用方的代碼又要修改,如此循環(huán)下去,是不是要抓狂了?隨著不斷添加新語種,代碼會(huì)變得越來越復(fù)雜,越來越難以維護(hù)。這樣的代碼耦合性非常高,是不合理的,也就是出現(xiàn)了所謂的代碼的壞味道,你可以通過設(shè)計(jì)模式(如觀察者模式等),在不使用委托的情況下來重構(gòu)代碼,但是實(shí)現(xiàn)起來是非常麻煩的,要寫很多更多的代碼...
委托可以傳遞方法,而這些方法可以代表一系列的操作,這些操作都由調(diào)用方來決定,就很好擴(kuò)展了,而且十分靈活。我們不會(huì)對(duì)已有的方法進(jìn)行修改,而是只以添加方法的形式去進(jìn)行擴(kuò)展。
可能有人又會(huì)說,我直接在調(diào)用方那里來一個(gè)一個(gè)調(diào)用我要執(zhí)行哪些方法一樣可以實(shí)現(xiàn)這樣的效果啊?
可你有沒有想過,你要調(diào)用的是一系列方法,你根本無法復(fù)用這一系列的方法。使用委托就不一樣了,它好比一個(gè)方法集合的容器,你可以往里面增減方法,可以復(fù)用的。而且使用委托,你可以延時(shí)方法列表的調(diào)用,還可以隨時(shí)對(duì)方法列表進(jìn)行增減。委托對(duì)方法進(jìn)行了再一次的封裝。
總結(jié):也就是當(dāng)你只能確定方法的函數(shù)簽名,無法確定方法的具體執(zhí)行時(shí),為了能夠更好的擴(kuò)展,以類似于注入方法的形式來實(shí)現(xiàn)新增的功能,就能體現(xiàn)出委托的價(jià)值。
委托和直接調(diào)用函數(shù)的區(qū)別:用委托就可以指向任意的函數(shù),哪怕是之前沒定義的都可以,而不用受限于哪幾種。
多播委托
組合的委托必須是同一個(gè)類型,其相當(dāng)于創(chuàng)建了一個(gè)按照組合的順序依次調(diào)用的新委托對(duì)象。委托的組合一般是給事件用的,用普通委托的時(shí)候很少用。
通過+來實(shí)現(xiàn)將方法添加到委托實(shí)例中,-來從委托實(shí)例中進(jìn)行方法的移除。
+和-純粹是為了簡(jiǎn)化代碼而生的,實(shí)際上其調(diào)用的分別是Delegate.Combine方法和Delegate.Remove。
如果委托中存在多個(gè)帶返回值的方法,那么調(diào)用委托的返回值是最后一個(gè)方法的返回值。
public static void MultipleShow()
{
//多播委托
NoParaWithReturnEventHandler _NoParaWithReturnEventHandler = new NoParaWithReturnEventHandler(GetDateTime);
_NoParaWithReturnEventHandler += GetDateTime;
Console.WriteLine(_NoParaWithReturnEventHandler());
}
public static string GetDateTime()
{
return string.Format("今天是{0}號(hào)。", DateTime.Now.Day.ToString());
}
委托總結(jié):
- 委托封裝了包含特殊返回類型和一組參數(shù)的行為,類似包含單一方法的接口;
- 委托類型聲明中所描述的類型簽名決定了哪個(gè)方法可用于創(chuàng)建委托實(shí)例,同時(shí)決定了調(diào)用的簽名;
- 為了創(chuàng)建委托實(shí)例,需要一個(gè)方法以及(對(duì)于實(shí)例方法來說)調(diào)用方法的目標(biāo);
- 委托實(shí)例是不易變的,就像String一樣;
- 每個(gè)委托實(shí)例都包含一個(gè)調(diào)用列表——一個(gè)操作列表;
- 事件不是委托實(shí)例——只是成對(duì)的add/remove方法(類似于屬性的取值方法/賦值方法)。
常見使用場(chǎng)景:窗體傳值、線程啟動(dòng)時(shí)綁定方法、lambda表達(dá)式、異步等等。
生活中的例子:現(xiàn)在不是大家都在搶火車票嗎,使用云搶票就相當(dāng)于使用委托,你可以直接自己買票,也可以托管于云搶票,自己搶票的話,在快要開槍的時(shí)候,你必須時(shí)刻刷新,下單輸驗(yàn)證碼等等,使用云搶票的話,你只要放票前,提前輸入搶票信息,就再也不需要你管了,自動(dòng)出票,你根本不需要知道云搶票那邊是怎么幫你實(shí)現(xiàn)搶票的。相同時(shí)間和車次可以做成一個(gè)委托實(shí)例,有很多人都通過這個(gè)委托實(shí)例來進(jìn)行搶票操作。
源碼下載:http://pan.baidu.com/s/1dEPlxJj
以上就是本文的全部內(nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來一定的幫助,同時(shí)也希望多多支持我們!
上一篇:C# using三種使用方法
欄 目:C#教程
下一篇:相對(duì)路徑和絕對(duì)路徑的寫法總結(jié)
本文標(biāo)題:詳解C#中的委托
本文地址:http://www.jygsgssxh.com/a1/C_jiaocheng/6015.html
您可能感興趣的文章
- 01-10C#中查找Dictionary中的重復(fù)值的方法
- 01-10C#將圖片存放到SQL SERVER數(shù)據(jù)庫中的方法
- 01-10C#中Socket通信用法實(shí)例詳解
- 01-10關(guān)于nancy中的身份驗(yàn)證
- 01-10C#中的事務(wù)用法實(shí)例分析
- 01-10C#裝箱和拆箱原理詳解
- 01-10C#類的多態(tài)性詳解
- 01-10C#創(chuàng)建不規(guī)則窗體的4種方式詳解
- 01-10C#實(shí)現(xiàn)讀取DataSet數(shù)據(jù)并顯示在ListView控件中的方法
- 01-10C#中深度復(fù)制和淺度復(fù)制詳解


閱讀排行
- 1C語言 while語句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹的示例代碼(圣誕
- 3利用C語言實(shí)現(xiàn)“百馬百擔(dān)”問題方法
- 4C語言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 01-10C#通過反射獲取當(dāng)前工程中所有窗體并
- 01-10關(guān)于ASP網(wǎng)頁無法打開的解決方案
- 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ī)閱讀
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 01-11ajax實(shí)現(xiàn)頁面的局部加載
- 01-10delphi制作wav文件的方法
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 01-10C#中split用法實(shí)例總結(jié)
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 04-02jquery與jsp,用jquery
- 01-10使用C語言求解撲克牌的順子及n個(gè)骰子


