委托在本质上仍然是一个类我们用delegate关键字声明的所有委托都继承自System.MulticastDelegate。后者又是继承自System.Delegate类System.Delegate类则继承自System.Object。委托既然是一个类那么它就可以被定义在任何地方即可以定义在类的内部也可以定义在类的外部。正如很多资料上所说的委托是一种类型安全的函数回调机制 它不仅能够调用实例方法也能调用静态方法并且具备按顺序执行多个方法的能力。委托揭秘在把委托说透1中可以看到委托的使用其实是很简单的。尽管如此其内部实现仍然相当复杂。.NET强大的编译器和CLR掩盖了这种复杂性。为了解释方便我们把1中的委托代码复制在下面并做一处小小的改动将LogToTextFile设置为实例方法。namespace DelegateSample { public delegate void Log(string message); class UserService { public Log LogDelegate { get; set; } public UserService() { } public void Register(User user) { if (user.Name Kirin) { LogDelegate(注册失败已经包含名为 user.Name 的用户); } else { LogDelegate(注册成功); } } } class Program { static void Main(string[] args) { User user new User { Name Kirin, Password 123 }; UserService service new UserService(); service.LogDelegate LogToConsole; Program p new Program(); service.LogDelegate p.LogToTextFile; service.Register(user); Console.ReadLine(); } static void LogToConsole(string message) { Console.WriteLine(message); } void LogToTextFile(string message) { using (StreamWriter sw File.AppendText(log.txt)) { sw.WriteLine(message); sw.Flush(); sw.Close(); } } } }打开Reflector反编译Log委托可以看到Log类被编译为如下形式在上图中可以得出如下结论委托是一个类可以很清晰的看出Log—MulticastDelegate—Delegate这种继承机制。尽管委托继承自System.MulticastDelegate类但我们并不能显示地声明一个继承自System.MulticastDelegate类的委托。委托必须使用delegate关键字声明编译器会自动为我们生成继承代码。由于委托继承自System.MulticastDelegate类自然也继承MulticastDelegate类的字段、属性和方法。这些成员中最重要的当属三个非公共字段如下表所示字段名称字段类型描述_targetSystem.Object该字段指明委托所调用的方法所在的实例类型。如果委托调用的为静态方法该字段为null如果为实例方法则为该方法所在的对象。_methodPtrSystem.IntPtr标识回调方法的指针。_invocationListSystem.Object在构建委托链时指向一个委托数组在委托刚刚构建时通常为null。由上表可以看出每个委托对象实际上是对方法及其调用时操作的对象的封装。MulticastDelegate类还定义了两个只读公有实例属性Target和Method分别对应_target和_methodPtr。Target属性返回一个方法回调时操作的对象引用。如果是静态方法则返回null。Method属性返回一个标识回调方法的System.Reflection.MethodInfo对象。编译器自动为委托创建了BeginInvoke、EndInvoke和Invoke三个方法当我们在像调用普通的方法一样调用委托时如LogDelegate(注册失败已经包含名为 user.Name 的用户);这时实际上调用的是编译器自动生成的Invoke方法LogDelegate.Invoke(注册失败已经包含名为 user.Name 的用户);使用IL DASM查看UserService的IL代码可以验证以上结论如下图所示在使用委托时我们也可以显示调用Invoke方法CLR 2.0。Invoke方法的参数和返回值与委托是一致的。在调用Invoke方法时会使用_target和_methodPtr字段。BeginInvoke和EndInvoke方法用来实现异步调用本文在此不进行讨论。委托链委托链是一个委托的集合它允许我们调用这个集合中的委托所代表的所有方法对于有返回值的方法委托链的返回值为链表中最后一个方法的返回值本文后面会有详细介绍。在Delegate类中定义了3个静态方法来帮助我们操作委托链。public static Delegate Combine(params Delegate[] delegates); public static Delegate Combine(Delegate a, Delegate b); public static Delegate Remove(Delegate source, Delegate value);要理解委托链我们首先基于前面的例子重新声明两个委托logDel1和logDel2。Log logDel1 LogToConsole; Program p new Program(); Log logDel2 p.LogToTextFile;这两个委托的_target、_methodPtr和_invocationList值分别如下图所示构造委托链然后我们使用Combin方法来构造一个委托链Log logChain null; logChain (Log)Delegate.Combine(logChain, logDel1);由于logChain初始为null在使用Combin方法构造委托链时将返回另外一个参数logDel1再将logDel1的引用赋给logChain。这时logChain将指向logDel1所指向的对象。接下来我们将logDel2也添加到logChain中来logChain (Log)Delegate.Combine(logChain, logDel2);此时由于logChain已经不再是null将重新构建一个新的委托对象。该委托对象的_target和_methodPtr字段与logDel2第二个参数相同_invocationList字段将指向一个委托数组。该委托数组中包含两个元素第一个元素索引为0指向封装了LogToConsole方法的委托即logDel1指向的委托第二个元素索引为1指向封装了LogToTextFile方法的委托即logDel2指向的委托。最后将这个新创建的委托对象的引用赋给logChain。若再将一个新的委托logDel3添加到委托链中则仍然会构建一个新的委托对象并将logDel3的引用添加到该委托对象_invocationList的末尾此时链表共有3个元素。然后再将该委托对象的引用赋给logChain。而logChain之前指向的委托对象则等待垃圾回收。至此委托链构造完毕我们来看看如何执行委托链表中的委托。由于logChain仍然指向一个委托对象因此执行委托链表的语法与执行委托是一样的logChain(执行委托链);与普通的委托如logDel1所不同的是logChain的_invocationList字段不为null。这时将首先遍历执行_invocationList中的所有委托。所执行的方法的顺序与添加的顺序一致依次为LogToConsole、LogToTextFile。委托Log的Invoke方法的实现用伪代码表示如下public void Invoke(string message) { Delegate[] delegateSet _InvocationList as Delegate[]; if (delegateSet ! null) { // 如果委托数组不为空则依次执行该委托数组中的委托 foreach (Feedback d in delegateSet) d(value); }