链方法[C# 基础知识系列]专题三:如何用委托包装多个方法——委托链
最近研究鏈方法,稍微總結(jié)一下,以后繼續(xù)補(bǔ)充:
????
弁言:
????
?上一專題分析了下編譯器是如何來翻譯委托的,從中間語言的角度去看委托,希望可以幫助大家進(jìn)一步的理解委托,然而之前的分析都是委托只是封裝一個(gè)方法,那委托能不能封裝多個(gè)方法呢?因?yàn)樯钪薪?jīng)常會(huì)聽到,我代表大家的意見等這樣的談話,既然委托也是一個(gè)代表,那他如果只能代表一個(gè)人,那他的魅力就不是很大了吧,所以我們就會(huì)委托能不能代表多個(gè)方法的? 答案是可以的,這就是本專題要講的內(nèi)容——委托鏈,委托鏈也是一個(gè)委托,只是因?yàn)樗前讯鄠€(gè)委托鏈在一起,所以我們就以委托鏈來這么稱呼它的。
????
?
????
一、到底什么是委托鏈
????
我們平常實(shí)例化委托對(duì)象時(shí)都是綁定一個(gè)方法的, 前一個(gè)專題分析的委托也是包裝了一個(gè)方法的, 用后面的例子就是委派律師的只有一個(gè)人,也就是當(dāng)事人只有一個(gè)的,但是現(xiàn)實(shí)生活中明顯不是這樣的,在訟事的時(shí)候律師可以同時(shí)接多個(gè)案子,也是接收多個(gè)當(dāng)時(shí)人的委派,這樣,該律師就與多個(gè)當(dāng)事人綁定在一起了, 須要懂得多個(gè)當(dāng)事人的案件情況的。其實(shí)這就是生活中的委托鏈,此時(shí)這位律師不僅僅是一個(gè)人的代表律師了,而是多個(gè)當(dāng)事人的律師。生活中的委托鏈和C#中的委托鏈很類似的,當(dāng)初就說說C#中的委托鏈究竟是個(gè)什么的?
????
首先委托鏈就是一個(gè)委托,所以大家不要看到委托鏈感覺又是什么C#中的新特性的,然而要把多個(gè)委托鏈在一起,就必須存儲(chǔ)多個(gè)委托的引用,那委托鏈對(duì)象是在哪里存儲(chǔ)多個(gè)委托的引用的呢?還記得我們上一專題中,我們分析的委托類型有三個(gè)非公共字段的嗎?這三個(gè)字段是——_target,methodPtr 和_invocationList,至于這三個(gè)字段具體代表什么大家可以查看我的上一專題的文章,然而_invocationList 字段正是存儲(chǔ)多個(gè)委托引用的地方的。
????
為了更好的解釋_invocationList是如何來存儲(chǔ)委托引用的,下面先看一個(gè)委托鏈的例子和運(yùn)行結(jié)果,然后再分析原因:
????
?
using System;namespace DelegateTest{public class Program{// 聲明一個(gè)委托類型,它的實(shí)例引用一個(gè)方法// 該方法回去一個(gè)int 參數(shù),返回void類型public delegate void DelegateTest(int parm);public static void Main(string[] args){// 用靜態(tài)方法來實(shí)例化委托DelegateTest dtstatic = new DelegateTest(Program.method1);// 用實(shí)例方法來實(shí)例化委托DelegateTest dtinstance = new DelegateTest(new Program().method2);// 隱式調(diào)用委托dtstatic(1);// 顯式調(diào)用Invoke方法來調(diào)用委托dtinstance.Invoke(1);// 隱式調(diào)用委托dtstatic(2);// 顯式調(diào)用Invoke方法來調(diào)用委托dtinstance.Invoke(2);Console.Read();}private static void method1(int parm){Console.WriteLine("調(diào)用的是靜態(tài)方法,參數(shù)值為:" + parm);}private void method2(int parm){Console.WriteLine("調(diào)用的是實(shí)例方法,參數(shù)值為:" + parm);}}}????
運(yùn)行結(jié)果:
????
????
下面就來分析下為什么會(huì)涌現(xiàn)這樣的結(jié)果的:
????
一開始我們實(shí)例化了兩個(gè)委托變量,如下代碼:
// 用靜態(tài)方法來實(shí)例化委托DelegateTest dtstatic = new DelegateTest(Program.method1);// 用實(shí)例方法來實(shí)例化委托DelegateTest dtinstance = new DelegateTest(new Program().method2);????
委托變量dtstatic和dtinstance引用的委托對(duì)象的初始狀態(tài)如下圖:
????
????
然后我們定義了一個(gè)委托類型的引用變量delegatechain,剛開始它沒有任何委托對(duì)象,是一個(gè)空引用,當(dāng)我們執(zhí)行下面的一行代碼時(shí),
delegatechain = (DelegateTest)Delegate.Combine(delegatechain, dtstatic);????
Combine方法發(fā)明試圖合并的是null和dtstatic,在內(nèi)部,Combine直接返回dtstatic中的對(duì)象,此時(shí)delegatechain和dtstatic變量引用的都是同一個(gè)委托對(duì)象,如下圖所示:
????
????
為了演示委托鏈,我們通過代碼在再添加一個(gè)委托,此時(shí)就再調(diào)用了Combine方法,代碼如下:
delegatechain = (DelegateTest)Delegate.Combine(delegatechain, dtinstance);????
這時(shí)候,Combine方法發(fā)明delegatechain已引用了一個(gè)委托對(duì)象了(此時(shí)已引用了destatic引用的委托對(duì)象了),所以Combine會(huì)結(jié)構(gòu)一個(gè)新的委托對(duì)象(這一點(diǎn)很想String.Concat,我們簡略的使用是通過+操作符把兩個(gè)字符串連接起來,關(guān)于字符串的討論大家可以參考我博客中的這篇文章http://www.cnblogs.com/zhili/archive/2012/06/25/String_StringBuilder.html),這個(gè)新的委托對(duì)象會(huì)對(duì)它的私有字段_target 和_methodPtr字段停止初始化,然后此時(shí)_invocationList字段初始化為引用了一個(gè)委托對(duì)象的數(shù)組,這個(gè)數(shù)組的第一個(gè)元素(下標(biāo)為0)就是被初始化為引用包裝了method1方法的委托,數(shù)組的二個(gè)元素被初始化為引用包裝了method2方法的委托(也就是dtinstance引用的委托對(duì)象),最后delegaechain被設(shè)為引用新建的這個(gè)委托對(duì)象,下面是一個(gè)圖,可以幫助大家理解委托鏈(也叫多播委托):
????
每日一情理這濃濃的母愛使我深深地認(rèn)識(shí)到:即使你是一只矯健的雄鷹,也永久飛不出母愛的長空;即使你是一條揚(yáng)帆行駛的快船,也永久駛不出母愛的長河!在人生的路上不管我們已走過多遠(yuǎn),還要走多遠(yuǎn),我們都要經(jīng)過母親精心營造的那座橋!
????
同樣的情理,如果是添加第三個(gè)委托給委托鏈,進(jìn)程也是和下面一樣的, 此時(shí)又會(huì)新建一個(gè)委托對(duì)象,此時(shí)_invocationList字段會(huì)初始化為引用一個(gè)保存這三個(gè)委托對(duì)象數(shù)組,然而有人會(huì)問了——對(duì)于已引用了委托對(duì)象的委托類型變量調(diào)用Combine方法后會(huì)創(chuàng)建一個(gè)新的委托對(duì)象,然后對(duì)新的這個(gè)委托對(duì)象的三個(gè)字段停止重新初始化話,最后把之前的委托類型變量引用新創(chuàng)建的委托對(duì)象(這里就幫大家總結(jié)下委托鏈的創(chuàng)建進(jìn)程),那之前的委托對(duì)象怎么辦呢? 相信大部分人會(huì)有這個(gè)疑問的,這點(diǎn)和字符串的Concat方法很像,之前的委托對(duì)象和——invocationList字段引用的數(shù)組會(huì)被垃圾回收失落(正是因?yàn)檫@樣,委托和字符串String一樣是不可變的)。
????
注意:我們還可以調(diào)用Delegate的Remove方法從鏈中刪除委托,如調(diào)用下面代碼時(shí):
delegatechain =(DelegateTest)Delegate.Remove(delegatechain,new DelegateTest(method1));????
Remove方法被調(diào)用時(shí),它會(huì)掃描delegateChain(第一個(gè)參數(shù))所引用的委托對(duì)象內(nèi)部維護(hù)的委托數(shù)組(如果對(duì)于委托數(shù)組為空的情況下調(diào)用Remove方法將不會(huì)有任何作用,就是不會(huì)刪除任何委托引用,這里主要是說明掃描是從委托數(shù)組里停止掃描),如果找到delegateChain引用的委托對(duì)象的_target和_methodPtr字段
????
和第二個(gè)參數(shù)(新創(chuàng)建的委托)中的字段匹配的委托,如果刪除之后數(shù)組中只剩下一個(gè)數(shù)據(jù)項(xiàng)時(shí),就返回那個(gè)數(shù)據(jù)項(xiàng)(而不會(huì)去新建一個(gè)委托對(duì)象再初始化的,此時(shí)的_invocationList為null,而不是保存一個(gè)委托對(duì)象引用的數(shù)組了,具體可以Remove一個(gè)后調(diào)試看看的),如果此時(shí)數(shù)組中還剩余多個(gè)數(shù)據(jù)項(xiàng),就新建一個(gè)委托對(duì)象——其中創(chuàng)建并初始化_invocationList數(shù)組(此時(shí)的數(shù)組引用的委托對(duì)象已少了一個(gè)了,因?yàn)橛肦emove方法刪除了),并且,每次Remove方法調(diào)用只能從鏈中刪除一個(gè)委托,而不會(huì)刪除有匹配的_target和_methodPtr字段的所有委托(這個(gè)大家可以調(diào)試看看的)
????
?
????
二、如何對(duì)委托鏈中的委托調(diào)用停止控制
????
通過下面相信大家可以理解如何創(chuàng)建一個(gè)委托鏈對(duì)象的,但是從運(yùn)行結(jié)果中還可以看出,每次調(diào)用委托鏈時(shí),委托鏈包裝的每一個(gè)方法都會(huì)次序被執(zhí)行,如果委托鏈中被調(diào)用的委托拋出一個(gè)異常,這樣鏈中的后續(xù)所有對(duì)象都不能被調(diào)用,并且如果委托的后面擁有一個(gè)非void的返回類型,則只有最后一個(gè)返回值會(huì)被保存,其他所有回調(diào)方法的返回值都會(huì)被舍棄,這就意味著其他所有操作的返回值都永久看不到的嗎? 事實(shí)卻不是這樣的,我們可以通過調(diào)用Delegate.GetInvocationList方法來顯式調(diào)用鏈中的每一個(gè)委托,同時(shí)可以添加一些自己的定義輸出。
????
GetInvocationList方法返回一個(gè)由Delegate引用構(gòu)成的數(shù)組,其中每一個(gè)數(shù)組都指向鏈中的一個(gè)委托對(duì)象。在內(nèi)部,GetInvocationList創(chuàng)建并初始化一個(gè)數(shù)組,讓數(shù)據(jù)的每一個(gè)元素都引用鏈中的一個(gè)委托,然后返回對(duì)該數(shù)組的一個(gè)引用。如果_invocatinList字段為null,返回的數(shù)組只有一個(gè)元素,該元素就是委托實(shí)例本身。下面就通過一個(gè)程序來演示下的:
namespace DelegateChainDemo {class Program{// 聲明一個(gè)委托類型,它的實(shí)例引用一個(gè)方法// 該方法回去一個(gè)int 參數(shù),返回void類型public delegate string DelegateTest();static void Main(string[] args){// 用靜態(tài)方法來實(shí)例化委托DelegateTest dtstatic = new DelegateTest(Program.method1);// 用實(shí)例方法來實(shí)例化委托DelegateTest dtinstance = new DelegateTest(new Program().method2);DelegateTest dtinstance2 = new DelegateTest(new Program().method3);// 定義一個(gè)委托鏈對(duì)象,一開始初始化為null,就是不代表任何方法(我就是我,我不代表任何人)DelegateTest delegatechain = null;delegatechain += dtstatic;delegatechain += dtinstance;delegatechain += dtinstance2;delegatechain =(DelegateTest)Delegate.Remove(delegatechain,new DelegateTest(method1));delegatechain = (DelegateTest)Delegate.Remove(delegatechain, new DelegateTest(new Program().method2));Console.WriteLine(Test(delegatechain));Console.Read();}private static string method1(){return "這是靜態(tài)方法1";}private string method2(){throw new Exception("拋出了一個(gè)異常");}private string method3(){return "這是實(shí)例方法3";}// 測(cè)試調(diào)用委托的方法private static string Test(DelegateTest chain){if (chain == null){return null;}// 用這個(gè)變量來保存輸出的字符串StringBuilder returnstring = new StringBuilder();// 獲取一個(gè)委托數(shù)組,其中每一個(gè)元素都引用鏈中的委托Delegate[] delegatearray=chain.GetInvocationList();// 遍歷數(shù)組中的每一個(gè)委托foreach (DelegateTest t in delegatearray){try{//調(diào)用委托獲得返回值returnstring.Append(t() + Environment.NewLine);}catch (Exception e){returnstring.AppendFormat("異常從 {0} 方法中拋出, 異常信息為:{1}{2}", t.Method.Name, e.Message, Environment.NewLine);}}// 把結(jié)果返回給調(diào)用者return returnstring.ToString();}} }????
運(yùn)行結(jié)果截圖:
????
????
從運(yùn)行結(jié)果可以看出,此時(shí)我們可以獲得每一個(gè)回調(diào)方法的返回值,并且可以加入一些自定義的返回值的(程序中加入了換行字符串),這樣便可以對(duì)委托鏈中的每一個(gè)委托對(duì)象停止控制了,即使其中一個(gè)拋出異常,此時(shí)我們也可以停止捕獲,而不會(huì)致使后續(xù)的委托對(duì)象不能被調(diào)用的問題。
????
?
????
三、總結(jié)下
????
本專題主要分析如何創(chuàng)建一個(gè)委托鏈以及對(duì)于創(chuàng)建一個(gè)委托鏈的進(jìn)程停止了具體的分享,第二部分主要先指出了委托了一些局限性,然后通過調(diào)用GetInvocationList方法來返回一個(gè)委托數(shù)組,這樣便可以通過遍歷委托數(shù)組中的每一個(gè)委托來通知委托的調(diào)用進(jìn)程,這樣便可以對(duì)委托鏈的調(diào)用停止更多的控制的。到此本專題也就分析完了,通過這三個(gè)專題對(duì)委托的分析,相信大家會(huì)對(duì)委托有一個(gè)更深的理解,然后為什么要寫三個(gè)專題來具體分析委托的呢? 主要是后面要分析的事件,Lambda表達(dá)式,Linq方面的內(nèi)容都是和委托有關(guān)系的,所以更好的理解委托將是后面特性的一個(gè)基本,希望這些對(duì)大家有幫助,我將在下一個(gè)專題里頭分析事件。
文章結(jié)束給大家分享下程序員的一些笑話語錄: 程序員的愿望
有一天一個(gè)程序員見到了上帝.上帝: 小伙子,我可以滿足你一個(gè)愿望.程序員: 我希望中國國家隊(duì)能再次打進(jìn)世界杯.
上帝: 這個(gè)啊!這個(gè)不好辦啊,你還說下一個(gè)吧!
程序員: 那好!我的下一個(gè)愿望是每天都能休息6個(gè)小時(shí)以上.
上帝: 還是讓中國國家打進(jìn)世界杯.
--------------------------------- 原創(chuàng)文章 By
鏈和方法
---------------------------------
轉(zhuǎn)載于:https://www.cnblogs.com/jiangu66/p/3153266.html
總結(jié)
以上是生活随笔為你收集整理的链方法[C# 基础知识系列]专题三:如何用委托包装多个方法——委托链的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 179元起!OPPO首款智能摄像头可以侦
- 下一篇: c# char unsigned_dll