委託聲明和定義了一種引用類型,這種類型具有自己的簽名並能夠封裝靜態函數和實例的方法,一旦爲委託分配了方法,委託將具有和該方法具有完全相同的行爲。委託類似 C++ 的函數指針,但是委託是類型安全的。打個比方: 如果市長出差,那麼他就會委派他的祕書代理他的日常事務,此時祕書就擁有了和市長的一樣的權利,他就能暫時代理市長的事務。此時祕書就成了"委託類型"。
二) 原型聲明:
public delegate void TestDelegate(string message);
先看下面的簡單例子:
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編譯後生成的中間語言:
2{
3 .entrypoint
4 // Code size 134 (0x86)
5 .maxstack 3
6 .locals init ([0] class SampleDelegate d1,
7 [1] class SampleDelegate d2,
8 [2] class SampleClass sampleclass,
9 [3] class SampleDelegate d3,
10 [4] class 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中事件模型
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方法。