.net 委托的揭秘

从表面看,委托似乎很容易使用:用C#的delegate关键字定义,用熟悉的new操作符构造委托实例,用熟悉的方法调用语法来调用回调函数(只是要用引用了委托对象的一个变量来替代方法名)。

然而,实际情况比前面几个例子演示的要复杂一些。编译器和CLR在幕后做了大量工作来隐藏复杂性。首先让我们重新审视下面一行代码:

internal delegate void Feeback(Int32 value)

看到这行代码后,编译器实际会像下面这样定义一个完整的类:

internal class Feeback : System.MulticastDelegate{
		public Feeback(Object object, IntPtr method);
		
		public virtual void Invoke(Int32 value);

		public virtual IAsyncResult BeginInoke(Int32 value, AsyncCallBack callback, Object object);

		public virtual void EndInvoke(IAsyncResult result);
}

编译器定义的类有4个方法:一个构造器、Invoke、BeginInvoke和EndInvoke 现在重点解释构造器和Invoke方法。
事实上,用ILDASM.exe查看生成的程序集,验证编译器真的会自动生成这个类。

在这个例子中,编译器定义了一个名为Feedback的类,该类派生自FCL中定义的System.MulticastDelegate类型(所有委托类型都派生自MulticastDelegate)。

注意,所有的委托都有一个构造器,它要获取两个参数:一个是对象的引用,另一个是引用回调方法的一个整数。然后,如果仔细查看前面的源代码,会发现传递的是FeedbackToConsole或p.FeedbackToFile这样的值。根据迄今为止学到的编程知识,似乎没有可能通过编译!

然而,C#编译器知道要构造的是委托,所以会分析源代码来确定引用的是哪个对象和方法。对象引用被传给构造器的object参数,标识了方法的一个特殊IntPtr值(从MethodDef或MeberRef元数据token获得)被传给构造器的method参数。对于静态方法,会为object参数传递null值。在构造器内部,这两个实参分别保存在_target和_methodPtr私有字段中。除此以外,构造器还将_invocationList字段设为null。

所以,每个委托对象实际都是一个包装器,其中包含了一个方法和调用该方法时要操作的一个对象。例如,在执行以下两行代码之后:
Feeback fbStatic = new Feedback(Program.FeedbackToConsole);
Feedback fbInstance =new Feedback(new Program().FeebackToFile);

fbStatic 和 fbInstance 变量将引用两个独立的,初始化好的Feedback委托对象

Delegate类定义了两个只读公共实例属性:Target和Method。给定一个委托对象引用,可以查询这些属性。Target属性返回一个引用,它指向回调方法要操作的对象。简单地说,Target 属性返回保存在私有字段_target中的值。如果委托对象包装的是一个静态方法,Target将返回null。Method属性返对一个System.Reflection.MethodInfo对象的引用,该对象标识了回调方法。简单地说,Method属性有一个内部转换机制,能将私有字段_methodPtr中的值转换为一个MethodInfo对象并返回它。

可通过多种方式利用这些属性。例如可检查委托对象引用的是不是在一个特定类型中定义的实例方法:

Boolean DelegateRefersToInstanceMethodOfType(MulticastDelegate d, Type type){
	return ((d.Target != null) && d.Target.GetType() == type);
}

还可写代码检查回调方法是否有一个特定的名称(比如FeedbackToMsgBox)

Boolean DelegateReferToMethodOfName(MulticastDelegate d, string methodName){
	return (d.Method.Name == methodName);
}

这些属性还有其他潜在的应用。

知道了委托对象如何构造并了解其内部结构之后,再来看看回调方法时如何调用的。为方便讨论,下面重复了Counter方法的定义:

private static void Counter(Int32 from, Int32 to, Feedback fb){
	for(Int32 val = from; val <= to; val++){
		if(fb != null) fb(val);
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值