CLR via C# 委托 委托揭秘

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

编译器和CLR在幕后做了大量工作来隐藏复杂性。

首先重新审视这一行代码:

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

(ILDasm.exe)

编译器定义的类有4个方法:一个构造器、Invoke、BeginInvoke和EndInvoke。

(所有委托类型都派生自MulticastDelegate)。

重要提示: System.MulticastDelegate派生自System.Delegate,后者又派生自System.Object。是历史原因造成有两个委托类。这是在是令人遗憾---FCL本该只有一个委托类。没有办法,我们对这两个类都要有所了解。即使创建的所有委托类都将MulticastDelegate作为基类,个别情况下仍会使用Delegate类定义的方法处理自己的委托类型。例如,Delegate类的两个静态方法Combine和Remove的签名都指出要获取Delegate参数。由于你创建的委托类型派生自MulticastDelegate,后者又派生自Delegate,所以你的委托类型的实例是可以传给这两个方法的。

这个类的可访问性是private,因为委托在源代码中声明为internal。如果源代码改成使用public可见性,编译器生成的Feedback类也会变成公共类。要注意的是,委托类即可嵌套在一个类型中定义,也会在全局范围中定义。简单地说,由于委托是类,所以凡是能够定义类的地方,都能定义委托。

由于所有委托类型都派生自MulticastDelegate,所以它们继承了MulticastDelegate的字段、属性和方法。在所有这些成员中,有三个非公共字段是最重要的。

MulticastDelegate的三个重要的非公共字段
字段类型说明
_targetSystem.Object当委托对象包装一个静态方法时,这个字段为null。当委托包装一个实例方法时,这个字段引用的是回调方法要操作的对象。换言之,这个字段指出要给实例方法的隐式参数this的值。
_methodPtrSystem.IntPtr一个内部的整数值,CLR用它标识要回调的方法。
_invocationListSystem.Object该字段通常为null。构造委托链时它引用一个委托数组。

注意,所有委托都有一个构造器,它获取两个参数:一个是对象引用,另一个是引用了回调方法的整数。但是我们前面代码中传递的是Program.StaticFeedbackToConsole或p.InstanceFeedbackToConsole这样的值。根据我们的编成知识,这似乎没有可能通过编译。

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

所以,每个委托对象实际都是一个包装器,其中包装了一个方法和调用该方法时要操作的对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值