.NET Framework源码研究系列之---Delegate
前言
曾幾何時(shí)能看到微軟產(chǎn)品的源碼簡(jiǎn)直是天方夜譚,不過(guò)現(xiàn)在這卻成了現(xiàn)實(shí),微軟終于對(duì)外開(kāi)放了它的產(chǎn)品的源代碼.拋去開(kāi)源運(yùn)動(dòng)與微軟之間的世代情仇,拋去微軟這一做法的初衷,這總歸是件好事,能夠讓我們撥開(kāi)云霧,一窺優(yōu)秀產(chǎn)品的秘密.
前兩天看到有位仁兄在隨筆中的留言,說(shuō)他以為".NET中的設(shè)計(jì)模式"是在講.NET Framework與設(shè)計(jì)模式的關(guān)系,其實(shí)不是,不過(guò)這也讓我想起來(lái)自己確實(shí)研究過(guò).NET Framework的源碼,于是就找打算找時(shí)間把自己的心得體會(huì)拿出來(lái)和大家一起分享.
今天就先從最容易讓人困惑的委托(delegate)開(kāi)始,讓我們步入.NET Framework源碼世界,共同學(xué)習(xí)優(yōu)秀的程序設(shè)計(jì).
先看委托的定義:用于聲明一個(gè)引用類型,該引用類型可用于封裝命名方法或匿名方法。委托類似于 C++ 中的函數(shù)指針;但是,委托是類型安全和可靠的。
相信看到這段話之后,很多人,包括我自己就開(kāi)始一起探索委托與函數(shù)指針,于是各種網(wǎng)文就出現(xiàn)了.但委托到底是什么呢?我們先看一段很簡(jiǎn)單的代碼:
?
public delegate void OnAction(int flag);這里我們定義了一個(gè)最簡(jiǎn)單的委托:OnAction.MSDN解釋Delegate 類是委托類型的基類,但只有系統(tǒng)和編譯器可以顯式地從 Delegate 類或 MulticastDelegate 類派生.那么我們可以認(rèn)為OnAction是從delegate繼承過(guò)來(lái)的,只是不能顯式的繼承,由系統(tǒng)代做了.
接下來(lái)讓我們看一下微軟是怎么定義委托的:
?
[Serializable()][ClassInterface(ClassInterfaceType.AutoDual)]
[System.Runtime.InteropServices.ComVisible(true)]
public abstract class Delegate : ICloneable, ISerializable
由此可以看出delegate是個(gè)抽象類,并且實(shí)現(xiàn)了?ICloneable, ISerializable兩個(gè)接口,并且有ClassInterface(ClassInterfaceType.AutoDual)這么一個(gè)屬性.這有很多問(wèn)題.
首先,委托是個(gè)抽象類,所以要使用的必須繼承,但是委托又跟整型一樣,是一種類型,由此就可以理解問(wèn)什么"Delegate 類是委托類型的基類,但只有系統(tǒng)和編譯器可以顯式地從 Delegate 類或 MulticastDelegate 類派生"這句話的意思了,因?yàn)椴豢赡軓恼屠^承過(guò)來(lái)一個(gè)子類,那么委托為什么是一個(gè)類而不像整型一樣是一個(gè)結(jié)構(gòu)呢?這個(gè)問(wèn)題下面回答.
其次,這也是我覺(jué)得不理解的地方,委托實(shí)現(xiàn)了ICloneable, ISerializable兩個(gè)接口,也就是說(shuō)委托可以被克隆和序列化.相信大家沒(méi)人會(huì)寫OnAction.Clone();這么一句話.淺表克隆一個(gè)委托實(shí)在有點(diǎn)費(fèi)解.不過(guò),如果可以從良好的編程習(xí)慣上解釋為什么事先ICloneable接口的話,委托對(duì)ISerializable的實(shí)現(xiàn)就更讓人困惑了,因?yàn)榭赡芤虼藢?dǎo)致一些莫名其妙的編譯時(shí)錯(cuò)誤.我曾經(jīng)遇到這樣一個(gè)Bug,一個(gè)標(biāo)記序列化的單件實(shí)體類包含一個(gè)事件(事件默認(rèn)實(shí)現(xiàn)了ISerializable),該事件導(dǎo)致序列化的時(shí)候在凡是跟該事件的引用有關(guān)的地方全部報(bào)出莫名其妙的未標(biāo)記序列化的錯(cuò)誤,最終的解決辦法是需要將該事件標(biāo)記為[NonSerialized].
????? 下面看一下具體是怎么實(shí)現(xiàn)委托的:
?
代碼 public abstract class Delegate : ICloneable, ISerializable{
//要調(diào)用的對(duì)象
internal Object _target;
// MethodBase, either cached after first request or assigned from a DynamicMethod
internal MethodBase _methodBase;
// _methodPtr is a pointer to the method we will invoke
// It could be a small thunk if this is a static or UM call
internal IntPtr _methodPtr;
// In the case of a static method passed to a delegate, this field stores
// whatever _methodPtr would have stored: and _methodPtr points to a
// small thunk which removes the "this" pointer before going on
// to _methodPtrAux.
internal IntPtr _methodPtrAux;
// This constructor is called from the class generated by the
// compiler generated code
protected Delegate(Object target, String method)
{
if (target == null)
throw new ArgumentNullException("target");
if (method == null)
throw new ArgumentNullException("method");
// This API existed in v1/v1.1 and only expected to create closed
// instance delegates. Constrain the call to BindToMethodName to
// such and don't allow relaxed signature matching (which could make
// the choice of target method ambiguous) for backwards
// compatibility. The name matching was case sensitive and we
// preserve that as well.
if (!BindToMethodName(target, Type.GetTypeHandle(target), method,
DelegateBindingFlags.InstanceMethodOnly |
DelegateBindingFlags.ClosedDelegateOnly))
throw new ArgumentException(Environment.GetResourceString("Arg_DlgtTargMeth"));
}
// This constructor is called from a class to generate a
// delegate based upon a static method name and the Type object
// for the class defining the method.
protected unsafe Delegate(Type target, String method)
{
if (target == null)
throw new ArgumentNullException("target");
if (!(target is RuntimeType))
throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeType"), "target");
if (target.IsGenericType && target.ContainsGenericParameters)
throw new ArgumentException(Environment.GetResourceString("Arg_UnboundGenParam"), "target");
if (method == null)
throw new ArgumentNullException("method");
// This API existed in v1/v1.1 and only expected to create open
// static delegates. Constrain the call to BindToMethodName to such
// and don't allow relaxed signature matching (which could make the
// choice of target method ambiguous) for backwards compatibility.
// The name matching was case insensitive (no idea why this is
// different from the constructor above) and we preserve that as
// well.
BindToMethodName(null, target.TypeHandle, method,
DelegateBindingFlags.StaticMethodOnly |
DelegateBindingFlags.OpenDelegateOnly |
DelegateBindingFlags.CaselessMatching);
}
// Protect the default constructor so you can't build a delegate
private Delegate()
{
}
上面代碼顯示委托類包含4個(gè)internal類型的字段,從其注釋我們大致可以看出_target是我們要調(diào)用的對(duì)象,_methodBase是給委托賦值時(shí)傳遞的方法,_methodPtr是指向該方法的指針,_methodPtrAux同靜態(tài)類型委托的methodPtr指向的內(nèi)容一樣,只是少了對(duì)象的引用. 在看委托類的三個(gè)構(gòu)造函數(shù),一個(gè)是私有的,是用來(lái)防止實(shí)例化的,另外2個(gè)差不多,做的事情是把一個(gè)方法綁定到一個(gè)對(duì)象上.這里的方法應(yīng)該是我們賦給委托的方法,對(duì)象則是編譯器自己來(lái)維護(hù)的對(duì)象.這里調(diào)用了一個(gè)方法BindToMethodName,在源碼中看不到實(shí)現(xiàn),僅能看到如下內(nèi)容:
[MethodImplAttribute(MethodImplOptions.InternalCall)]private extern bool BindToMethodName(Object target, RuntimeTypeHandle methodType, String method, DelegateBindingFlags flags);
MethodImplAttribute標(biāo)簽表明了該方法為CLR內(nèi)部方法,具體是什么,我們不得而知.此類的方法還有很多,就不一一例舉.說(shuō)到這個(gè)地方,我們可以發(fā)現(xiàn)上面說(shuō)的"OnAction是從delegate繼承過(guò)來(lái)的"和"Delegate 類是委托類型的基類"并不正確,正確的理解應(yīng)該是delegate類在內(nèi)部管理了一個(gè)用于綁定方法的對(duì)象,這點(diǎn)也可以從OnAction反匯編出來(lái)的構(gòu)造函數(shù)看出:
代碼 .method public hidebysig specialname rtspecialnameinstance void .ctor(object 'object',
native int 'method') runtime managed
{
} // end of method OnAction::.ctor
另外delegate中還有一個(gè)靜態(tài)方法:
public static Delegate CreateDelegate(Type type, Object firstArgument, MethodInfo method, bool throwOnBindFailure)
該方法前后有11個(gè)重載,其方法體里無(wú)一不包括:
?
代碼 Delegate d = InternalAlloc(type.TypeHandle);if (!d.BindToMethodInfo(firstArgument, method.MethodHandle, method.DeclaringType.TypeHandle,
DelegateBindingFlags.RelaxedSignature))
{
if (throwOnBindFailure)
throw new ArgumentException(Environment.GetResourceString("Arg_DlgtTargMeth"));
d = null;
}
這樣一段代碼.其中InternalAlloc,BindToMethodInfo與BindToMethodName一樣,都屬于CLR內(nèi)部方法.具體是什么一樣看不出,只能大致猜測(cè)CLR內(nèi)部生成了一個(gè)委托對(duì)象,然后將我們定義的委托簽名,方法,方法引用等一些內(nèi)容交給委托類來(lái)維護(hù).
?
通過(guò)以上分析,我們發(fā)現(xiàn)如它的定義,委托本質(zhì)上是一個(gè)類,它的職責(zé)是代替我們管理維護(hù)方法調(diào)用.這一切無(wú)論從功能,還是外面,其實(shí)跟函數(shù)指針沒(méi)有半點(diǎn)關(guān)系.至此,相信大家對(duì)delegate有了不一樣的認(rèn)識(shí),同時(shí)也發(fā)現(xiàn)C#僅僅是CLR的一層外殼,并沒(méi)有涉及到很核心的內(nèi)容.
?
?
?
?
?????? 以上僅僅是我個(gè)人的一點(diǎn)看法,由于本人水平有限,認(rèn)識(shí)上難免有不正確的地方,如果你有更好的見(jiàn)解,敬請(qǐng)指正.
轉(zhuǎn)載于:https://www.cnblogs.com/niyw/archive/2010/07/22/1783306.html
總結(jié)
以上是生活随笔為你收集整理的.NET Framework源码研究系列之---Delegate的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: ActiviteX 服务控件
- 下一篇: 笔记:设计模式(3)-Abstract