主要包括:
一、 委托定义
二、 协变与反协变(Covariance and contra-variance)
三、 委托实现原理
四、 链式委托
注:本随笔小部分从MSDN引用而来,主要部分(包括代码和文字等)从本书总结而来。
示例程序:
delegateSample
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.IO;
5
6namespace TestDelegate
7{
8 //声明一个委托类型,它的实例引用一个方法,该方法取一个int32类型的参数,并返回void
9 internal delegate void Feedback(Int32 value);
10
11 //internal class Feedback:System.MulticastDelegate
12 //{
13 // public Feedback(Object obj,IntPtr method);
14
15 // public virtual void Invoke(Int32 value);
16
17 // //允许有异步回调的方法
18 // public virtual IAsyncResult BeginInvoke(Int32 value, AsyncCallback callback, Object obj);
19 // public virtual void EndInvoke(IAsyncResult result);
20
21 //}
22 public sealed class Program
23 {
24 static void Main(string[] args)
25 {
26 StaticDelegateDemo();
27 InstanceDelegateDemo();
28 ChainDelegateDemo1(new Program());
29 ChainDelegateDemo2(new Program());
30 }
31
32 private static void StaticDelegateDemo()
33 {
34 Console.WriteLine("---------------static Delegate Demo-----------------");
35 Counter(1, 3, null);
36 Counter(1, 3, new Feedback(Program.FeedbackToConsole));
37 Counter(1, 3, new Feedback(FeedbackToMsgBox));
38
39 Console.WriteLine();
40 }
41 private static void InstanceDelegateDemo()
42 {
43 Console.WriteLine("---------------Instance Delegate Demo-----------------");
44 Program p = new Program();
45 Counter(1, 3, new Feedback(p.FeedbackToFile));
46 Console.WriteLine();
47 }
48
49 private static void ChainDelegateDemo1(Program p)
50 {
51 Console.WriteLine("---------------Chain Delegate Demo1-----------------");
52 Feedback fb1 = new Feedback(FeedbackToConsole);
53 Feedback fb2 = new Feedback(FeedbackToMsgBox);
54 Feedback fb3 = new Feedback(p.FeedbackToFile);
55
56 Feedback fbChain = null;
57 fbChain = (Feedback)Delegate.Combine(fbChain, fb1);
58 fbChain = (Feedback)Delegate.Combine(fbChain, fb2);
59 fbChain = (Feedback)Delegate.Combine(fbChain, fb3);
60 Counter(1, 2, fbChain);
61 Console.WriteLine("\nremove:");
62 fbChain = (Feedback)Delegate.Remove(fbChain, new Feedback(FeedbackToMsgBox));
63 Counter(1, 2, fbChain);
64
65 }
66
67 private static void ChainDelegateDemo2(Program p)
68 {
69 Console.WriteLine("---------------Chain Delegate Demo2-----------------");
70 Feedback fb1 = new Feedback(FeedbackToConsole);
71 Feedback fb2 = new Feedback(FeedbackToMsgBox);
72 Feedback fb3 = new Feedback(p.FeedbackToFile);
73
74 Feedback fbChain = null;
75 fbChain += fb1;
76 fbChain += fb2;
77 fbChain += fb3;
78 Counter(1, 2, fbChain);
79
80 Console.WriteLine("\nremove:");
81 fbChain -= new Feedback(FeedbackToMsgBox);
82 Counter(1, 2, fbChain);
83 }
84
85 private static void Counter(Int32 from,Int32 to,Feedback fb)
86 {
87 for(Int32 val=from;val<=to;val++)
88 {
89 if(fb!=null)
90 {
91 fb(val);
92 }
93 }
94 }
95
96 private static void FeedbackToConsole(Int32 value)
97 {
98 Console.WriteLine("ConItem=" + value);
99 }
100 private static void FeedbackToMsgBox(Int32 value)
101 {
102 Console.WriteLine("MsgItem=" + value);
103 }
104 private void FeedbackToFile(Int32 value)
105 {
106 Console.WriteLine("SWItem=" + value);
107 }
108 }
109}
110
一、委托定义
委托是一种引用方法的类型,相当于C++中回调函数(CALLBACK)编程机制。与委托的签名(由返回类型和参数组成)匹配的任何方法都可以分配给该委托,只要知道委托的签名,便可以分配自己的委托方法。委托是类型安全的,允许顺序调用多个方法,不管是静态还是实例方法。
二、协变与反协变(Covariance and contra-variance)
1. 将一个方法绑定到一个委托时,C#和CLR允许引用类型的协变与反协变。协变指的是一个方法能返回从委托的返回类型那个派生的一个类型。反协变指的是一个方法的参数类型可以是委托的参数类型的基类。
如:delegate object MyCallBack(FileStream s);可以有如下原型的方法:string SomeMethod(Strem s);
2. 注意,协变与反协变只能用于引用类型,不能用于值类型,原因是因为值类型的存储结构是变化的,而引用类型的存储结构始终是一个指针,在进行值类型的如上转换时,编译器会报错
三、委托实现原理
1. 实现原理:
对于代码行:internal delegate void Feedback(Int32 value);
编译器实际上会定义一个完整的类:
internal class Feedback:System.MulticastDelegate
{
public Feedback(Object obj,IntPtr method);
public virtual void Invoke(Int32 value);
//允许有异步回调的方法
public virtual IAsyncResult BeginInvoke(Int32 value, AsyncCallback callback, Object obj);
public virtual void EndInvoke(IAsyncResult result);
}
编译器定义的类有四个方法:一个构造函数、Invoke,BeginInvoke和EndInvoke方法。BeginInvoke和EndInvoke在我的另外一篇关于异步调用的随笔中涉及过,有兴趣的可以看看。通过ISDasm.exe我们可用查看我们生成的程序集来验证我们的想法是不是正确。同时由这点我们也就知道了为什么委托可以定义在一个类型中定义也可以在全局范围内定义的根本原因了,因为它就是一个类,在可以定义类的地方都可以定义委托。
所有的委托都有构造函数,它读取两个参数,一个是对象引用,另一个是引用回调方法的一个整数。对象引用传递给obj参数,一个用来标识被传递的方法的IntPtr值(从MethodDef或MemberRef元数据标记中获得)被传递给method参数。静态方法obj为null,在构造器内部,obj和method两个参数分别保存在_target和_methodPtr私有字段内。另外构造器还有一个_invocationlist字段,该字段为一个引用委托对象的数组,它主要用于链式委托,同时也是用来作为垃圾回收的依据.
2. 回调函数调用原理
对于代码:
if(fb!=null)
{
fb(val);
}
在这里,因为编译器知道fb是一个引用一个委托对象的变量,所以会生成代码调用该委托对象的Invoke方法。即fb(val)在编译时其实调用的为:fb.Invoke(val);要具体查看可以打开它的IL代码进行查看。
四、链式委托
链式委托是由一系列的对象组成的集合,它允许调用集合中各个委托所表示的所有方法。
对于链式委托,对数据结构有所了解的应该都知道,链式存储结构一个是data域,一个是next域的节点,它本身在内存中拥有一个地址,data域在这里用来表示一个委托对象的引用和所引用的方法(即_target,_methodPtr),它的next域用来指向下一个委托节点对象,只是这里的next域是一个引用数组(指针数组),即前面说过的_invocationList字段,它包含了所有附加到链表上的委托对象的地址。一旦我们将所要调用的方法附加到某个委托后,该字段就会进行记录,保存你附加的每个委托的地址,然后在调用的时候循环调用。当对委托链表中的委托对象进行移除时,该列表也会做相应的调整。
添加一个委托对象:Delegate.Combine (Delegate, Delegate) //将两个委托的调用列表连接在一起
删除一个委托对象:Delegate.Remove (Delegate, Delegate) //从一个委托的调用列表中移除另一个委托的最后一个调用列表