重温委托[delegate]

来源:互联网 发布:java笔试题库及答案 编辑:程序博客网 时间:2024/06/07 21:00

 

一) 定义

委托声明和定义了一种引用类型,这种类型具有自己的签名并能够封装静态函数和实例的方法,一旦为委托分配了方法,委托将具有和该方法具有完全相同的行为。委托类似 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中事件模型
原创粉丝点击