1.认识委托
在.NET框架中,回调函数仍然像在非托管Windows编程中一样有用和普遍。但是,.NET框架为回调函数提供了一种称为委托的类型安全的机制。
在这里,我copy一下书籍上的一个很好的例子:
namespace @delegate { class Set { private Object[] items; public Set(int numItems) { items = new Object[numItems]; for (int i = 0; i < numItems; i++) items[i] = i; } //定义一个FeedBakc委托类型 //注意,该类型嵌套在Set 类中 public delegate void Feedback(object value, int item, int numItems); public void ProcessItems(Feedback feeback) { for (int i = 0; i < items.Length; i++) { if (feeback != null) { feeback(items[i], i + 1, items.Length); } } } } class Program { static void Main(string[] args) { StaticCallBacks(); InstanceCallBacks(); } private static void InstanceCallBacks() { //定义一个set集合 Set setOfItems = new Set(5); Program appobj = new Program(); setOfItems.ProcessItems(new Set.Feedback(appobj.FeedbackToFile)); } private static void StaticCallBacks() { Set setOfItems = new Set(5); //不会有反馈 setOfItems.ProcessItems(null); Console.WriteLine(); //反馈到终端上 setOfItems.ProcessItems(new Set.Feedback(Program.FeedbackToConsole)); Console.WriteLine(); //反馈到Messagebox上 setOfItems.ProcessItems(new Set.Feedback(Program.FeedbackToMsgBox)); Console.WriteLine(); //同时反馈到终端上和Messagebox上 Set.Feedback fb = null; fb += new Set.Feedback(Program.FeedbackToConsole); fb += new Set.Feedback(Program.FeedbackToMsgBox); Console.WriteLine(); } public static void FeedbackToConsole(object value, int item, int numItems) { Console.WriteLine("Processing item{0}of {1}:{2}.", item, numItems, value); } public static void FeedbackToMsgBox(object value, int item, int numItems) { MessageBox.Show(string.Format("Processing item{0}of {1}:{2}.", item, numItems, value)); } public void FeedbackToFile(object value, int item, int numItems) { StreamWriter sw = new StreamWriter("Status", true); sw.WriteLine("Processing item{0}of {1}:{2}.", item, numItems, value); sw.Close(); } } }
2.使用委托回调静态方法
也就是上面的StaticCallBacks()的调用。
3.使用委托回调实例方法
也即是上面InstanceClassbacks()函数的调用。
4.委托揭秘
public delegate void Feedback(object value,int item ,int numItems);
当遇到这段代码时,它会产生如下所示的一个完整的类定义:
public class Feedback:System.MulticastDelegate{
//构造器
public Feedback(object target,int methodPtr);
//下面的方法和源代码中指定的原型一样
public void virtual Invoke(object value,int item,item numItems);
//下面两个方法允许我们队委托进行异步回调
public virtual IAsyncResult ReginInvoke(object value,int item,int numItems,
AsyncCallback callback,object object);
public virtual void EndInvoke(IAsyncResult result);
}
所有的委托类型都继承自MulticastDelegate,他们自然也机继承了MulticastDelegate的字段、属性和方法。
MulticastDelegate中几个重要的私有字段
_target System.objet 指向回调函数被调用时应该被操作的对象,该字段用于实例化方法的回调
_methodPtr System.Int32 一个内部的整数值,CLR用它来标示回调函数
_prev System.MulticastDelegate指向另一个委托对象,该字段通常为null
MulticastDelegate类定义了两个只读实例属性:Target和Method。给定又给委托对象的引用,我们可以查询这些属性。Target属性反转一个方法回调时操作的对象引用。如果是静态方法,Target将返回null。Method属性返回一个标示回调方法的System.Reflection.MethodInfo对象。
注意所有的委托都有一个构造器,并且该构造器接受两个参数:一个对象引用和一个指向回调方法的整数。编译器通过分析源代码确定我们引用的是哪个对象和方法。其中的对象引用会被传递给target参数,一个特殊的标示方法的int32的值(由MethodDef或者MethodRef元数据标记获得)会被传递给methdPtr参数。对于静态方法而已,null会被传递给target参数。
下面看看如果回调函数是如何调用的。
当编译器遇到feedback(items[i],i+1,items.Length);
它产生的代码就像编译下面的源代码一样:
feedback.Invoke(items[i],i+1,items.Length);
5.委托史话:System.Delegate与System.MulticastDeleatge
在设计.NET框架时,提供了两种委托:一种是单播委托,一种是多播委托。他们期望继承自MulticastDelegate的类型来表示可以被连接在一起的委托对象,而继承自Delegate的类型来表示不可以被链接在一起的委托对象。
System.Delegate被设计为一个基本类型,它实现了回调一个函数所有必需的功能,MulticastDelegate类继承自Deleaget,并且为创建MulticastDelegate对象链表提供了支持。具体来讲,那些签名具有非void返回值的方法原型所表示的委托继承自System.Delegate,而使那些签名具有void返回值的方法原型所表示的委托继承自System.MulticastDelegate。
6.委托判等
Delegate重写了Object的Equals虚方法,MulticastDelegate又重写了Delegate的Equals实现。MulticastDelegate重写了Equals方法在比较两个委托对象事会首先看他们的_target和_methodPtr字段是否偶指向同一的对象和方法。如果这两个字段不匹配,那么Equals返回false。如果这连个字段都匹配,那么在看看连个委托对象是否表示委托链表的头部也就是说他们的_prev字段不为null。如果连个委托对象的_prev字段指示的链表有相同的长度,并且两个链表对应委托对象的_target和_methodPtr字段也都匹配,那么Equals将返回true,否则将返回false。)
7.委托链
MulticastDelegate对象都有以俄国私有字段_prev。该字段为指向另一个MulticastDelegate对象的引用。也就是说,每个MulticastDelegate对象都有一个指向另一个MulticstDelegate对象的引用,这使得多个委托对象可以组合成为一个链表。
Delegae类中定义了3个静态方法来帮助我们操作委托链表:
Class System.Delegate
{
//组合head和tail所表示的链表,并返回head。注意:head将是最后一个被调用的委托对象
public static Delegate Combine(Delegate tail,Delegate head);
//创建一个由委托数组表示的委托链表。注意索引为0的元素将为链表头部,并且将是最后一个被调用的委托对象
public static Delegate Combine(Delegate [] delegateArray);
//从链表中移除一个和value的回调目标/回调方法想匹配的委托,新的链表头会被返回,并且将是最后一被调用的委托对象
public static Delegate Remove(Delegate source,Delegate value);
}
当一个委托对象呗调用时,编译器会产生对该委托类型的Invoke方法的调用
(伪码描述)
class Feedback:MulticastDelegate
{
public void virtual Invoke(object value,int32 item,int32 numItems)
{
if(_prev!=null)_prve.Invoke(value,item,numItems);
_target.methodPtr(value,item,numItems);
}
}
8.C#对委托的支持
C#编译器自动为委托类型实例提供了+=和-=操作符重载支持。这两个操作符分别调用Delegate。Combine和Delegate.Remve方法。
9.对委托链调用施以更多的控制
MulticastDelegate类提供饿了一个实例方法GetInvocationList,我们可以施以它来显示调用委托链上的每一个委托读写,这事我们可以采用任何满足我们需要的算法。
10.委托与反射
System.Delegate提供了几个方法,允许我们在编译时不知道一些信息时仍能够创建调用又给委托
public class Deleaget
{
//创建一个疯子MethodInfo的delType委托
public static Delegate CreateDelegate(Type delType,MethodInfo mi);
//创建一个封装静态方法的delType委托
public static Deleaget CreateDelegaet(Type delType,Type type,stirng methodName);
//创建一个分支实例方法的delType委托
public static Delegate CreateDelegate(Type delegateType,Object obj,String methodName);
//创建一个分支实例方法的delType委托
public static Delegate CreateDeletgate(Type delegateType,Object obj,string methodName,boolean ignoreCase);
public Object DynamicInvoke(Object[] args);
}