詳解c 委託鏈

2022-09-26 01:54:09 字數 4006 閱讀 7521

引言:

上一專題介紹了下編譯器是如何來翻譯委託的,從中間語言的角度去看委託,希望可以幫助大家進一步的理解委託,然而之前的介紹都是委託只是封裝乙個方法,那委託能不能封裝多個方法呢?因為生活中經常會聽到,我代表大家的意見等這樣的說話,既然委託也是乙個代表,那他如果只能代表乙個人,那他的魅力就不是很大了吧,所以我們就會委託能不能代表多個方法的? 答案是可以的,這就是本專題要講的內容——委託鏈,委託鏈也是乙個委託,只是因為它是把多個委託鏈在一起,所以我們就以委託鏈來這麼稱呼它的。

一、到底什麼是委託鏈

我們平常例項化委託物件時都是繫結乙個方法的, 前乙個專題介紹的委託也是包裝了乙個方法的, 用前面的例子就是委派律師的只有乙個人,也就是當事人只有乙個的,但是現實生活中顯然不是這樣的,在官司的時候律師可以同時接多個案子,也是接收多個當時人的委派,這樣,該律師就與多個當事人繫結在一起了, 需要了解多個當事人的案件情況的。其實這就是生活中的委託鏈,此時這位律師不僅僅是乙個人的代表律師了,而是多個當事人的律師。生活中的委託鏈和c#中的委託鏈很類似的,現在就說說c#中的委託鏈到底是個什麼的?

首先委託鏈就是乙個委託,所以大家不要看到委託鏈感覺又是什麼c#中的新特性的,然而要把多個委託鏈在一起,就必須儲存多個委託的引用,那委託鏈物件是在**儲存多個委託的引用的呢?還記得我們上一專題中,我們介紹的委託型別有三個非公共欄位的嗎?這三個欄位是——_target,methodptr 和_invocationlist,至於這三個字段具體代表什麼大家可以檢視我的上一專題的文章,然而_invocationlist 字段正是儲存多個委託引用的地方的。

為了更好的解釋_invocationlist是如何來儲存委託引用的,下面先看乙個委託鏈的例子和執行結果,然後再分析原因:

using system;

namespace delegatetest

private static void method1(int parm)

private void method2(int parm)}}

執行結果:

下面就來分析下為什麼會出現這樣的結果的:

一開始我們例項化了兩個委託變數,如下**:

// 用靜態方法來例項化委託

delegatetest dtstatic = new delegatetest(program.method1);

// 用例項方法來例項化委託

delegatetest dtinstance = new delegatetest(new program().method2);

委託變數dtstatic和dtinstance引用的委託物件的初始狀態如下圖:

然後我們定義了乙個委託型別的引用變數delegatechain,剛開始它沒有任何委託物件,是乙個空引用,當我們執行下面的一行**時,

delegatechain = (delegatetest)delegate.combine(delegatechain, dtstatic);

combine方法發現試圖合併的是null和dtstatic,在內部,combine直接返回dtstatic中的物件,此時delegatechain和dtstatic變數引用的都是同乙個委託物件,如下圖所示:

這時候,combine方法發現delegatechain已經引用了乙個委託物件了(此時已經引用了destatic引用的委託物件了),所以combine會構造乙個新的委託物件(這一點很想string.concat,我們簡單的使用是通過+操作符把兩個字串連線起來,這個新的委託物件會對它的私有欄位_target 和_methodptr欄位進行初始化,然後此時_invocationlist欄位初始化為引用了乙個委託物件的陣列,這個陣列的第乙個元素(下標為0)就是被初始化為引用包裝了method1方法的委託,陣列的二個元素被初始化為引用包裝了method2方法的委託(也就是dtinstance引用的委託物件),最後delegaechain被設為引用新建的這個委託物件,下面是乙個圖,可以幫助大家理解委託鏈(也叫多播委託):

同樣的道理,如果是新增第三個委託給委託鏈,過程也是和上面一樣的, 此時又會新建乙個委託物件,此時_invocationlist欄位會初始化為引用乙個儲存這三個委託物件陣列,然而有人會問了——對於已經引用了委託物件的委託型別變數呼叫combine方法後會建立乙個新的委託物件,然後對新的這個委託物件的三個字段進行重新初始化話,最後把之前的委託型別變數引用新建立的委託物件(這裡就幫大家總結下委託鏈的建立過程),那之前的委託物件怎麼辦呢? 相信大部分人會有這個疑問的,這點和字串的concat方法很像,之前的委託物件和——invocationlist欄位引用的陣列會被垃圾**掉(正是因為這樣,委託和字串string一樣是不可變的)。

注意:我們還可以呼叫delegate的remove方法從鏈中刪除委託,如呼叫下面**時:

delegatechain =(delegatetest)delegate.remove(delegatechain,new delegatetest(method1));

remove方法被呼叫時,它會掃瞄delegatechain(第乙個引數)所引用的委託物件內部維護的委託陣列(如果對於委託陣列為空的情況下呼叫remove方法將不會有任何作用,就是不會刪除任何委託引用,這裡主要是說明掃瞄是從委託陣列裡進行掃瞄),如果找到delegatechain引用的委託物件的_target和_methodptr欄位

和第二個引數(新建立的委託)中的字段匹配的委託,如果刪除之後陣列中只剩下乙個資料項時,就返回那個資料項(而不會去新建乙個委託物件再初始化的,此時的_invocationlist為null,而不是儲存乙個委託物件引用的www.cppcns.com陣列了,具體可以remove乙個後除錯看看的),如果此時陣列中還剩餘多個資料項,就新建乙個委託物件——其中建立並初始化_invocationlist陣列(此時的陣列引用的委託物件已經少了乙個了,因為用remove方法刪除了),並且,每次remove方法呼叫只能從鏈中刪除乙個委託,而不會刪除有匹配的_target和_methodptr欄位的所有委託(這個大家可以除錯看看的)

二、如何對委託鏈中的委託呼叫進行控制

通過上面相信大家可以理解如何建立乙個委託鏈物件的,但是從執行結果中還可以看出,每次呼叫委託鏈時,委託鏈包裝的每個方法都會順序被執行,如果委託鏈中被呼叫的委託丟擲乙個異常,這樣鏈中的後續所有物件都不能被呼叫,並且如果委託的前面具有乙個非void的返回型別,則只有最後乙個返回值會被保留,其他所有**方法的返回值都會被捨棄,這就意味著其他所有操作的返回值都永遠看不到的嗎? 事實卻不是這樣的,我們可以通過呼叫deleg程式設計客棧ate.getinvocationlist方法來顯式呼叫鏈中的每乙個委託,同時可以新增一些自己的定義輸出。

getinvocationlist方法返回乙個由delegate引用構成的陣列,其中每乙個陣列都指向鏈中的乙個委託物件。在內部,getinvocationlist建立並初始化乙個陣列,讓資料的每乙個元素都引用鏈中的乙個委託,然後返回對該陣列的乙個引用。如果_invocatinlist欄位為null,返回的陣列只有乙個元素,該元素就是委託例項本身。下面就通過乙個程式來演示下的:

namespace delegatechaindemo

private static string method1()

private string method2()

private string method3()

// 測試呼叫委託的方法

private static string test(delegatetest chain)

// 用這個變數來儲存輸出的字串

stringbuilder returnstring = new stringbuilder();

// 獲取乙個委託陣列,其中每個元素都引用鏈中的委託

delegate delegatearray=chain.getinvocationlist();

// 遍歷陣列中的每個委託

foreach (delegatetest t in delegatearray)

catch (exception e)

方法中丟擲, 異常資訊為:", t.method.name, e.message, environment.newline);}}

// 把結果返回給呼叫者

return returnstring.tostring();

} }}

執行結果截圖:

三、總結下

C 委託,委託鏈,多播委託

委託 概念 委託是一種使用者自定義的型別.是一種類.可以把委託看成用來執行方法 函式的乙個東西 形式 delegate 函式返回型別 委託名稱 引數部分 例項化 委託型別 例項化名 new 委託型別 方法名稱 實現 usingsystem usingsystem.collections.generi...

C 委託詳解

c 委託 1.委託定義 委託是一種資料型別,和類是同級別的,delegate可以直接看著關鍵字class,我們可以直接將delegate看著為class,區別為class裡存放的是一系列方法,屬性,字段,事件,索引。而delegate裡存放的是一系列具有相同型別引數和返回回型別的方法的位址的位址。可...

C 委託詳解

c 委託 1.委託定義 委託是一種資料型別,和類是同級別的,delegate可以直接看著關鍵字class,我們可以直接將delegate看著為class,區別為class裡存放的是一系列方法,屬性,字段,事件,索引。而delegate裡存放的是一系列具有相同型別引數和返回回型別的方法的位址的位址。可...