重溫委託[delegate]

 

一) 定義

委託聲明和定義了一種引用類型,這種類型具有自己的簽名並能夠封裝靜態函數和實例的方法,一旦爲委託分配了方法,委託將具有和該方法具有完全相同的行爲。委託類似 C++ 的函數指針,但是委託是類型安全的。打個比方:  如果市長出差,那麼他就會委派他的祕書代理他的日常事務,此時祕書就擁有了和市長的一樣的權利,他就能暫時代理市長的事務。此時祕書就成了"委託類型"。 

二) 原型聲明:
public delegate void TestDelegate(string message);

先看下面的簡單例子:
 1using System;
 2
 3//聲明瞭一個委託,並定義了委託的簽名
 4delegate void SampleDelegate(string message);
 5
 6public class SampleClass
 7{
 8    //靜態方法
 9    static public void SampleStaticMethod(string message)
10    {
11        Console.WriteLine(message);
12    }

13
14    //類的實例方法
15    public void SampleObjectMethod(string message)
16    {
17        Console.WriteLine(message);
18    }

19}

20
21class MainClass
22{   
23    static void Main()
24    {
25        //可以將靜態函數分配給委託,也可以將匿名方法分配給委託,但都必須符合委託的簽名
26
27        //將靜態函數分配給委託
28        SampleDelegate d1 = new SampleDelegate(SampleClass.SampleStaticMethod);
29        //將靜態函數分配給委託
30        SampleDelegate d2 = SampleClass.SampleStaticMethod;
31        //將實例方法分配給委託
32        SampleClass sampleclass = new SampleClass();
33        SampleDelegate d3 = new SampleDelegate(sampleclass.SampleObjectMethod);
34        //將匿名方法分配給委託
35        SampleDelegate d4 = delegate(string message)
36        {
37            Console.WriteLine(message);
38        }
;
39
40        d1("d1");
41        d2("d2");
42        d3("d3");
43        d3("d4");
44
45        Console.ReadLine();
46    }

47}

輸出的結果:


通過上面的例子,我們可以看到使用委託要分三步走:

1) 聲明委託
       delegate void SampleDelegate(string message);

2) 實例化委託
        SampleDelegate d1 = new SampleDelegate(SampleClass.SampleStaticMethod);
         ..............
3) 回調委託
       d1("d1");
       ...................

委託可以回調三種方法:

1) 靜態方法
    SampleDelegate d1 = new SampleDelegate(SampleClass.SampleStaticMethod);

2) 類的實例方法
    SampleClass sampleclass = new SampleClass();
    SampleDelegate d3 = new SampleDelegate(sampleclass.SampleObjectMethod);

3) 匿名方法
    SampleDelegate d4 = delegate(string message)
        {
            Console.WriteLine(message);
        };

三) 委託的原理

反編譯剛纔的類,我們就可以看到如下結果:


在源程序中,我們並沒有定義SampleDelegate這個類,可是這卻有這樣一個類。 爲什麼呢?
這裏就引出了委託的祕密。
我們之前聲明的delegate void SampleDelegate(string message)這個委託,編譯器爲將編譯成一個完整的類。這個類中繼承了MulticastDelegate這個類,並且定義了三個虛方法。其中Invoke(string):void這個方法執行回調方法。

再看以下MainClass中的Main編譯後生成的中間語言:
 1.method private hidebysig static void  Main() cil managed
 2{
 3  .entrypoint
 4  // Code size       134 (0x86)
 5  .maxstack  3
 6  .locals init ([0class SampleDelegate d1,
 7           [1class SampleDelegate d2,
 8           [2class SampleClass sampleclass,
 9           [3class SampleDelegate d3,
10           [4class SampleDelegate d4)
11  IL_0000:  nop
12  IL_0001:  ldnull
13  IL_0002:  ldftn      void SampleClass::SampleStaticMethod(string)
14  IL_0008:  newobj     instance void SampleDelegate::.ctor(object,
15                                                           native int)
16  IL_000d:  stloc.0
17  IL_000e:  ldnull
18  IL_000f:  ldftn      void SampleClass::SampleStaticMethod(string)
19  IL_0015:  newobj     instance void SampleDelegate::.ctor(object,
20                                                           native int)
21  IL_001a:  stloc.1
22  IL_001b:  newobj     instance void SampleClass::.ctor()
23  IL_0020:  stloc.2
24  IL_0021:  ldloc.2
25  IL_0022:  ldftn      instance void SampleClass::SampleObjectMethod(string)
26  IL_0028:  newobj     instance void SampleDelegate::.ctor(object,
27                                                           native int)
28  IL_002d:  stloc.3
29  IL_002e:  ldsfld     class SampleDelegate MainClass::'<>9__CachedAnonymousMethodDelegate1'
30  IL_0033:  brtrue.s   IL_0048
31  IL_0035:  ldnull
32  IL_0036:  ldftn      void MainClass::'<Main>b__0'(string)
33  IL_003c:  newobj     instance void SampleDelegate::.ctor(object,
34                                                           native int)
35  IL_0041:  stsfld     class SampleDelegate MainClass::'<>9__CachedAnonymousMethodDelegate1'
36  IL_0046:  br.s       IL_0048
37  IL_0048:  ldsfld     class SampleDelegate MainClass::'<>9__CachedAnonymousMethodDelegate1'
38  IL_004d:  stloc.s    d4
39  IL_004f:  ldloc.0
40  IL_0050:  ldstr      "d1"
41  IL_0055:  callvirt   instance void SampleDelegate::Invoke(string)
42  IL_005a:  nop
43  IL_005b:  ldloc.1
44  IL_005c:  ldstr      "d2"
45  IL_0061:  callvirt   instance void SampleDelegate::Invoke(string)
46  IL_0066:  nop
47  IL_0067:  ldloc.3
48  IL_0068:  ldstr      "d3"
49  IL_006d:  callvirt   instance void SampleDelegate::Invoke(string)
50  IL_0072:  nop
51  IL_0073:  ldloc.3
52  IL_0074:  ldstr      "d4"
53  IL_0079:  callvirt   instance void SampleDelegate::Invoke(string)
54  IL_007e:  nop
55  IL_007f:  call       string [mscorlib]System.Console::ReadLine()
56  IL_0084:  pop
57  IL_0085:  ret
58}
 // end of method MainClass::Main


可見:
1)SampleDelegate d1 = new SampleDelegate(SampleClass.SampleStaticMethod)和SampleDelegate d2 = SampleClass.SampleStaticMethod經編譯器便宜後生成完全相同的中間代碼。
2)d1("d1"), d2("d2")等這些方法都會調用Invoke(string)方法。

四)委託的判等

先看下面兩個語句:
Console.WriteLine(d1.Equals(d2));   //顯示"True"
Console.WriteLine(d1.Equals(d3));   //顯示"False"
輸出是什麼呢?
假如回答都是False的話,那就大錯了。
委託的判等有點特殊,如果兩個委託他們指向的回調目標和回調方法相同,那麼結果就是"True".否則"False".
d1和d2顯然都是類SampleClass的靜態方法SampleStaticMethod。所以d1等於d2是毫無疑問的。

五)委託鏈



六) 委託在.NET中的使用場合

委託主要用於三種場合:
1) 異步回調
2) 多線程,使用委託來啓動多線程時調用的一個方法
3) .NET中事件模型

 1using System;
 2delegate void D(int x);
 3class C
 4{
 5    public static void M1(int i) {
 6        Console.WriteLine("C.M1: " + i);
 7    }

 8    public static void M2(int i) {
 9        Console.WriteLine("C.M2: " + i);
10    }

11    public void M3(int i) {
12        Console.WriteLine("C.M3: " + i);
13    }

14}

15class Test
16{
17    static void Main() 
18        D cd1 = new D(C.M1);
19        cd1(-1);                // call M1
20        Console.WriteLine();
21        D cd2 = new D(C.M2);
22        cd2(-2);                // call M2
23        Console.WriteLine();
24        D cd3 = cd1 + cd2;
25        cd3(1);                // call M1 then M2
26        Console.WriteLine();
27        cd3 += cd1;
28        cd3(2);                // call M1, M2, then M1
29        Console.WriteLine();
30        cd3 -= cd2;
31        cd3(3);
32        Console.ReadLine();
33    }

34}
輸出:

一目瞭然,C#能用"+="和"-="來實現委託鏈。其實"+="重載了Delegate.Combine,"-="則重載了Delegate.Remove方法。
六) 委託在.NET中的使用場合

委託主要用於三種場合:
1) 異步回調
2) 多線程,使用委託來啓動多線程時調用的一個方法
3)
.NET中事件模型
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章