欧美第一综合在线|国产黄色视频在线播放|国产一二卡三卡四卡免费|国产精品久久久久久五月尺|av制服丝袜无码一区二区|九九精品国产99国产精品|9久久伊人久久大香线蕉一区|欧美中文字幕一区二区三区亚洲

濟(jì)寧果殼科技專業(yè)軟件開發(fā)團(tuán)隊(duì),提供全方位的互聯(lián)網(wǎng)服務(wù)!
座機(jī):15563775221 手機(jī):15563775221(微信)

熱門標(biāo)簽

  1. 首頁
  2. 果殼學(xué)院
  3. 小程序開發(fā)
  4. .NET委托原理及使用
.NET委托原理及使用

.NET委托原理及使用

很多應(yīng)用程序中(C,C++),需要對象使用某種回調(diào)機(jī)制,能夠與創(chuàng)建它 的實(shí)體進(jìn)行通信,在.NET平臺下,通過委托來提供了一種回調(diào)函數(shù)機(jī)制

濟(jì)寧果殼科技

2017-04-10 11:15:55

0

2505

什么是委托? -> 初識委托
在很多應(yīng)用程序中(C,C++),需要對象使用某種回調(diào)機(jī)制,能夠與創(chuàng)建它 的實(shí)體進(jìn)行通信,在.NET平臺下,通過委托來提供了一種回調(diào)函數(shù)機(jī)制,在.NET平臺下,委托確?;卣{(diào)函數(shù)是類型安全的(這也正是.NET FreamWork與非托管代碼的區(qū)別)。本質(zhì)上來講,委托是一個類型安全的對象,它指向程序中另一個以后會被調(diào)用的方法(或多個方法),就是將方法作為 參數(shù)來傳遞.
C#中定義委托類型
在C#中創(chuàng)建一個委托類型時,需要使用關(guān)鍵字 delegate 關(guān)鍵字,類型名可以自定義,但是 委托要指定一個回調(diào)方法的簽名.
1 //聲明一個委托,該委托可以指向任何傳入兩個Int類型并且方法的返回值為Int.2 public delegate int Binary(int x,int y);3 4 //聲明一個委托,該委托可以指向任何傳入一個String類型并且方法返回值為Void5 public delegate void DelegateBackCall(string str);
使用委托發(fā)送對象狀態(tài)通知 -> 用委托回調(diào)靜態(tài)方法
先來看一段代碼:
void Main()
{
Program.Main();
}
public delegate void DelegateBackCall(int value);
class Program
{
public static void Main()
{
Counter(1,4,null);
Counter(1,4,new DelegateBackCall(StaticDelegateToConsole));
}
private static void Counter(int x,int y,DelegateBackCall foo) {
for(var i = x;i <= y;i++)
{
if(foo != null)
foo(i);21
}
}
private static void StaticDelegateToConsole(int num)
{
Console.WriteLine("Item : " + num);
}

}
復(fù)制代碼
首先 定義了一個名字為DelegateBackCall委托,該委托指定的方法要獲取Int類型參數(shù),返回void,在Program類中定義了私有的靜態(tài)方 法Counter,用來統(tǒng)計X到Y(jié)之間整數(shù)的個數(shù),同時呢,Counter方法還獲取一個Foo,Foo是對一個DelegateBackCall委托對 象的引用, 在方法體中 我們首先遍歷一下,如果Foo不為null,就調(diào)用Foo變量所指定的回調(diào)函數(shù).那么傳入的這個回調(diào)函數(shù)的是正在處理的那個數(shù)據(jù)項(xiàng)的值.
然后 在Program的Main函數(shù)中,第一次調(diào)用Counter時,第三個參數(shù)傳遞的是Null,所在在Counter函數(shù)中是不會執(zhí)行回調(diào)函數(shù)的.
接著第二次調(diào)用Counter函數(shù)時,給第三個參數(shù)傳遞一個新構(gòu)造的 DelegateBackCall委托對象(其實(shí)在此委托對象是方法的一個包裝器, 使方法能通過包裝器來間接的進(jìn)行回調(diào)),然后靜態(tài)方法StaticDelegateToConsole被傳給DelegateBackCll委托類型的構(gòu) 造器(StaticDelegateToConsole就是要包裝的方法),當(dāng)Counter執(zhí)行時,會在遍歷每個數(shù)據(jù)項(xiàng)之后調(diào)用靜態(tài)方法 StaticDelegateToConsole,最后輸出結(jié)果:
1 Result:2 Item : 1 3 Item : 24 Item : 3 5 Item : 4
使用委托發(fā)送對象狀態(tài)通知 -> 用委托調(diào)用實(shí)例方法
首先還是來看Main()函數(shù)中,第三個調(diào)用Counter的地方,在此將代碼貼出來吧.
復(fù)制代碼
1 class Program 2 { 3     public static void Main() 4     { 5         Counter(1,4,null); 6         Counter(1,4,new DelegateBackCall(StaticDelegateToConsole)); 7          8         Counter(1,4,new DelegateBackCall(new Program().InstanceDelegateToMessage)); 9     }10     private void InstanceDelegateToMessage(int num)11     {12         Console.WriteLine("Message : " + num);13     }14 }
復(fù)制代碼
在第三次調(diào)用Counter函數(shù)時,第三個參數(shù)傳遞的是Program創(chuàng)建的實(shí)例 方法,這將委托包裝對InstanceDelegateToMessage方法的一個引用,該方法是一個實(shí)例方法.同調(diào)用靜態(tài)的一樣,當(dāng)Counter調(diào) 用Foo回調(diào)的時候會調(diào)用InstanceDelegateToMessage實(shí)例方法,新構(gòu)造的對象將作為隱式的this參數(shù)傳給這個實(shí)例方法.
最后的輸出結(jié)果為:
1 Message : 12 Message : 23 Message : 34 Message : 4
通過上面兩個例子我們知道委托可以包裝對實(shí)例方法和靜態(tài)方法的調(diào)用,如果是實(shí)例方法,那么委托需要知道方法操作的是哪個對象實(shí)例(包裝實(shí)例是很有用的,對象內(nèi)部的代碼可以訪問對象的實(shí)例成員,這意味著對象可以維護(hù)一些狀態(tài),并在回調(diào)方法執(zhí)行期間利用這些狀態(tài)信心).
委托的協(xié)變和逆變
將一個方法綁定到委托時,C#和CLR都允許引用類型的協(xié)變和逆變
協(xié)變:方法的返回類型是從委托的返回類型派生的一個類型
逆變:方法獲取的參數(shù)類型是委托參數(shù)類型的基類.
例如:
1 delegate object CallBack(FileStream file);2 3 string SomeMethod(Stream stream);
在上面代碼中,SomeMethod的返回類型(string)派生自委托的返回類型(object),這樣協(xié)變是可以的.
SomeMethod的參數(shù)類型(Stream)是委托的參數(shù)類型(FileStream)的基類,這樣逆變是可以的.
那么如果將String SomeMethod(Stream stream) 改為 Int SomeMethod(Stream stream);
那么C#編輯器會報錯.
說明:協(xié)變性和逆變性只能用于引用類型,不能用于值類型和Void , 因?yàn)橹殿愋偷拇鎯Y(jié)構(gòu)是變化的,而引用類型的存儲結(jié)構(gòu)始終是一個指針.
委托和接口的逆變和協(xié)變 -> 泛型類型參數(shù)
委托的每個泛型類型參數(shù)都可標(biāo)記為協(xié)變量或者逆變量,這樣我們即可將泛型委托類型的一個變量轉(zhuǎn)型為同一個委托類型的另一個變量,或者的泛型參數(shù)類型不同。
不變量:表示泛型類型參數(shù)不能更改。
逆變量:表示泛型類型參數(shù)可以從一個基類更改為該類的派生類。在C#中,用in關(guān)鍵字標(biāo)記逆變量形式的泛型類型參數(shù),逆變量泛型參數(shù)只能出現(xiàn)在輸入位置.
協(xié)變量:表示泛型類型參數(shù)可以從一個派生類更改為它的基類,在C#中,用out標(biāo)記協(xié)變量形式的泛型類型參數(shù).協(xié)變量只能出現(xiàn)在輸出位置.例如:方法返回值類型.
public Delegate  TResult Func(T arg);
在上面這行代碼中,泛型類型參數(shù) T 用in關(guān)鍵字標(biāo)記,使它成為了一個逆變量,而TResult用out 關(guān)鍵字標(biāo)記,這使他成為了一個協(xié)變量.
在使用要獲取泛型參數(shù)和返回值的委托時,盡量使用逆變性和協(xié)變性指定in和out關(guān)鍵字.在使用具有泛型類型參數(shù)的接口也可將它的類型參數(shù)標(biāo)記為逆變量和協(xié)變量,如下代碼:
復(fù)制代碼
1 public interface  IEnumerator: IEnumerator 2 { 3     Boolean MoveNext(); 4     T Current{get;} 5 } 6 // count方法接受任意類型的參數(shù) 7 public int count(IEnumerablecoll){} 8  9 //調(diào)用count 傳遞一個IEnumerable10 int i = count(new[]{"Albin"});
復(fù)制代碼
深入委托 -> 委托揭秘
首先來聲明一個委托:
1 internal delegate void DelegateBackCall(int val);
通過查看委托反編譯:

通過反 編譯之后看到在DelegateBackCall來中有四個方法:分別為 :  構(gòu)造器、BeginInvoke、EndInvoke、Invoke  而且還能看到 DelegateBackCall 類是繼承 system.MulticaseDelegate(其實(shí)所有的委托都是派生自它.本質(zhì)上來講:System.MulticaseDelegate是派生 自system.Delegate.而后者又派生自system.object),
在此我們還看到 在 第一個構(gòu)造函數(shù)中,有兩個參數(shù),一個是對象引用,一個是引用回調(diào)方法的一個整數(shù),其實(shí)所有委托中都有一個構(gòu)造器,而且構(gòu)造器的參數(shù)正如剛才所說的(一個對象引用,一個引用回調(diào)方法的整數(shù)) 在構(gòu)造器的內(nèi)部,這兩個參數(shù)分別保存在_target(當(dāng) 委托對象包裝一個靜態(tài)方法時,這個字段為null,當(dāng)委托對象包裝一個實(shí)例方法時,這個字段引用的是回調(diào)方法要操作的對象.)和_method(一個內(nèi)部 的整數(shù)值,CLR用它來標(biāo)識要回調(diào)的方法) 這兩個私有字段呢給 ,此外構(gòu)造器還將_invocationList(構(gòu)造一個委托鏈時,它可以引用一個委托數(shù)組,通常為null)字段設(shè)為null
每個委托對象實(shí)際都是一個包裝器,其中包裝了一個方法和調(diào)用該方法時要操作的一個對象
如下代碼:
1 DelegateBackCall   DelegateInstance = new DelegateBackCall(Program.InstanceToConsole);2 3 DelegateBackCall   DelegateStatic  =  new DelegateBackCall(StaticToMessage);
在此 DelegateInstance 和 DelegateStatic 變量引用兩個獨(dú)立的,初始化好的 DelegatebackCall委托對象. 在委托類(Delegate)中定義了兩個只讀的公共實(shí)例屬性,Target和Method,當(dāng)我們給定一個委托對象引用可以查詢這些屬 性.,Target返回的就是我們之前說的_Target字段中的值,指向回調(diào)方法要操作的對象,即如果是一個靜態(tài)的方法那么Target為 null,Method屬性有一個內(nèi)部轉(zhuǎn)換機(jī)制,可以將私有字段_methodPtr中的值轉(zhuǎn)為為一個MethodInfo對象并返回它.即我們所傳遞的 方法名.
在我們調(diào)用委托對象的變量時,實(shí)際上編譯器所生成的代碼時調(diào)用的該委托對象的 Invoke方法,比如,在Counter函數(shù)中,Foo(val) 那么實(shí)際上編譯器為我們解析為 --> Foo.Invoke(val); Invoke是以一種同步的方式調(diào)用委托對象維護(hù)每一個方法.意思就是說:調(diào)用者必須等待調(diào)用完成才能繼續(xù)執(zhí)行.Invoke通過_Target和 _methodPtr在指定對象上調(diào)用包裝好的回調(diào)方法,Invoke方法的簽名與委托的簽名是一致的.
用委托回調(diào)很多方法 —> 委托鏈(多播委托)
委托鏈?zhǔn)怯晌袑ο髽?gòu)成的一個集合.利用委托鏈,可以調(diào)用集合中的委托所代表的任何方法.換句話說就是一個委托對象可以維護(hù)一個可調(diào)用方法的列表而不是單獨(dú)一個方法,給一個委托對象添加多個方法時,不用直接分配,在此C#編譯器為我們提供了重載 +=,-= 操作符,
那我們還是拿上一代代碼來舉例,代碼如下:
1 DelegateBackCall   DelegateInstance = new DelegateBackCall(Program.InstanceToConsole);2 3  DelegateBackCall   DelegateStatic  =  new DelegateBackCall(StaticToMessage);
這段代碼我們可以這樣來改裝一下:
1 DelegateBackCall  DelegateFb = null;2 3 DelegateFb += new DelegateBackCall(Program.InstanceToConsole);4 5 DelegateFb +=  new DelegateBackCall(StaticToMessage);
其實(shí)上面這就是委托鏈(也叫多播委托),
那么我們來反編譯一下:

我們在反編譯之后發(fā)現(xiàn), +=操作的內(nèi)部是通過 Delegate類的靜態(tài)方法Combine將委托添加到鏈中的.
在此Combine會構(gòu)造一個新的委托對象,這個新的委托對象對它的私有字段 _target和_methodPtr進(jìn)行初始化,  同時_invocationList字段被初始化為引用一個委托對象數(shù)組,數(shù)組的第一個元素被初始化為引用包裝了 Program.InstanceToConsole 方法的委托,數(shù)組的第二個元素被初始化為引用了包裝了StaticToMessage方法的委托,然后再內(nèi)部會進(jìn)行遍歷每一個委托進(jìn)行輸出.
同理當(dāng)我們想移除委托對象集合中一個委托時我們可以通過-= 操作符.如下
DelegateBackCall delegateFb = null;
delegateFb -= new DelegateBackCall(new Program().InstanceToConsole);
delegateFb -= new DelegateBackCall(StaticToMessage);
那么反編譯后同樣的道理:

它的內(nèi)部是通過Remove函數(shù)來對委托進(jìn)行移除的.在Remove方法被調(diào)用 時,它會遍歷所引用的那個委托對象內(nèi)部維護(hù)的委托數(shù)組,Remove通過查找其_target和methodPtr字段與第二個參數(shù)中的字段匹配的委托, 如果找到匹配的委托,并且在刪除之后數(shù)組中只剩余一個數(shù)據(jù)項(xiàng),就返回那個數(shù)據(jù)項(xiàng),如果找到,并且數(shù)組中還剩余多個數(shù)據(jù)項(xiàng),就新建一個委托對象 其中創(chuàng)建并初始化的_invocationList數(shù)組將引用原始數(shù)組中的所有數(shù)據(jù)項(xiàng)(被刪除的例外),如果刪除僅有的一個元素,那么Remove會返回 NULL.每次Remove只能刪除一個委托.不會刪除所有的.
在此說明一個方法:GetInvocationList,這個方法操作一個從 MulticastDelegate派生的對象,返回一個由Delegate引用構(gòu)成的數(shù)組,其中每個引用都指向委托鏈中的一個委托.在其內(nèi) 部,GetInvocationList構(gòu)造并初始化一個數(shù)組,讓它的每個元素都引用鏈中的一個委托,然后返回對數(shù)組的一個引用,如果 _invocationList字段為null,返回數(shù)組只有一個元素,那么它就是委托實(shí)例的本身.
如下代碼:
DelegateBackCall delegateFb = null;
delegateFb += new DelegateBackCall(new Program().InstanceToConsole);
delegateFb += new DelegateBackCall(StaticToMessage);private static void GetComponentReport(DelegateBackcall delegateFo)
{     if(delegatefo != null )
{
Delegate[] arrayDelegates = delegatefo.GetInvocationList();       foreach(DelegateBackCall delegateback in arrayDelegates){ //....省略 }    }
}
委托定義太多? —> 泛型委托
在.NET Freamework 支持泛型,所以我們可以定義泛型委托,來減少委托聲明的個數(shù),這樣可以減少系統(tǒng)中類型數(shù)目,同時也可以簡化編碼.

在.NET freamework中為我們提供了17個Action委托,它們從無參數(shù)一直到最多16個參數(shù),對于開發(fā)來說應(yīng)該是足夠用的了.
除此之外 .NET FreameWork 還提供了17個Function函數(shù),它們允許回調(diào)方法返回一個值.

使用獲取泛型實(shí)參和返回值的委托時,可利用逆變和協(xié)變,
委托的簡潔寫法 -> 1:Lambda表達(dá)式、匿名函數(shù)的方式來代替定義回調(diào)函數(shù)。
實(shí)際上這些的寫法可歸納為C#的語法糖,它為我們程序員提供了一種更簡單,更可觀方式.
例如:
不需要定義回調(diào)函數(shù),直接使用Lambda表達(dá)式的形式,創(chuàng)建匿名函數(shù)來執(zhí)行方法體.
Private Static Void CallBackNewDelegateObject()
{
ThreadPool.QueueUserWorkItem( o => Console.WriteLine(o) ,5);
}
傳給QueueUserWorkItem方法的第一個實(shí)參是一個Lambda表達(dá) 式,Lambda表達(dá)式可在編譯器預(yù)計會看到一個委托的地方使用,編譯器看到這個Lambda表達(dá)式之后會在類中自動定義一個新的私有方法,這個方法稱為 匿名方法,所謂匿名方法,并不是沒有名字,它的名字是由編譯器自動創(chuàng)建的.在此說明一點(diǎn),匿名函數(shù)是被標(biāo)記為Static,并且是private 這是因?yàn)榇a沒有訪問任何實(shí)例成員,不過類中可以引用任何靜態(tài)字段或靜態(tài)方法.從而課件匿名函數(shù)的性能是比實(shí)例方法效率高的,因?yàn)樗恍枰~外的this 參數(shù).
2:簡化語法 -> 局部變量不需要手動的包裝到類中即可傳給回調(diào)方法
public static void UsingLocalVariablesInTheCallBack(int num)
{
int[] i = new int[num]; 4
AutoResetEvent done = new AutoResetEvent(false);
for (int n = 0; n < i.Length; n++)
{
ThreadPool.QueueUserWorkItem(obj =>
{
int nums = (Int32)obj;
i[nums] = nums * nums;
if (Interlocked.Decrement(ref num) == 0)
{
done.Set();
}
}, n);
}
done.WaitOne();
for (int n = 0; n < i.Length; n++)
{
Console.WriteLine("Index {0},i {1}", n, i[n]);
}
}
在Lambda表達(dá)式的方法體中,如果這個方法體是一個單獨(dú)的函數(shù),那么我們?nèi)绾?的將變量的值傳到方法中呢?那么現(xiàn)在在我們的匿名函數(shù)方法體中的代碼就需要抽出到一個單獨(dú)的類中,然后類通過字段賦值每一個值,然 后 UsingLocalVariablesInTheCallBack 方法必須構(gòu)造這個類的一個實(shí)例,用方法定義的局部變量的值來初始化這個實(shí)例中的字段,然后構(gòu)造綁定到實(shí)例方法的委托對象。

本文出自 51CTO “Albin” 博客

更多知識請進(jìn)入【濟(jì)寧果殼學(xué)院】

定制首選 果殼科技 咨詢熱線:0537-2551991

關(guān)閉咨詢框