C#中的委托解析
谈及到C#的基本特性,“委托”是不得不去了解和深入分析的一个特性。对于大多数刚入门的程序员谈到“委托”时,都会想到“将方法作为方法的参数进行传递”,很多时候都只是知道简单的定义,主要是因为“委托”在理解上有较其他特性比较难的地方。在本次说明中,不会将委托的简单声明和调用作为重点。
“委托”不需要直接定义一个要执行的行为,而是将这个行为用某种方法“包含”在一个对象中。这个对象可以像其他任何对象那样使用。在该对象中,可以执行封装的操作。可以选择将委托看作之定义了一个方法的接口,将委托的实例看作实现了那个接口的对象。
在“委托”的相关定义中,我们可以不难看出,“委托与方法“相比较于“接口与类”有着设计理念上的相似部分,产生的背景源于”设计原则“中的”开放-封闭原则“,”开放-封闭“原则:是说软件实体(类,模块,函数等等)应该可以扩展,但是不可修改。换一种说法可能更好的理解”对于扩展是开放的,对于更改是封闭的“,面对新的需求,对于程序的改动是通过增加新的代码进行的,而不是更改现有的代码。
在C#中委托用delegate关键字定义,使用new操作符构造委托实例,采用传统的方法调用语法来回调函数(只是要用引用了委托对象的一个变量代替方法名)。在C#中,委托在编译的时候会被编译成类。对于委托的一个说明:委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递。委托类既可嵌套在一个类型中定义,也可以在全局范围内定义。由于委托是类,凡是可以定义类的地方,都可以定义委托。
接下来我们来看一下”委托“的组成,需要满足的条件:
1.声明委托类型。
2.必须有一个方法包含了要执行的代码。
3.必须创建一个委托实例。
4.必须调用委托实例。
接下来大致的了解一下上面所提出的4项条件:
委托类型实际上只是参数类型的一个列表以及返回类型。规定了类型的实例能表示的操作。在调用一个委托实例的时候,必须保证使用的参数完全匹配,而且能以指定的方式使用返回值。对于委托实例的创建,取决于操作使用实例方法还是静态方法(如果操作是静态方法,指定类型名称就可以,如果是操作实例方法,需要先创建类型的实例)。对于委托的调用,可以直接调用委托的实例的方法就可以完成对应的操作。
以上谈及了”委托“的定义和组成,接下来我们来了解一下如何将方法绑定到”委托“上,以及委托的合并和删除。
可以将多个方法赋给同一个委托,委托实例实际有一个操作列表与之关联。在System.Delegate类型中提供了两个静态方法Combine()和Remove()负责委托实例的新增和删除操作。但是在我们的实际开发中,较多的采用-=和+=操作符。
在FCL中,所有的委托类型都派生自MulticastDelegate,该类型在System.MulticastDelegate类型中。
具体来看一下Combine()方法的底层实现代码:
[System.Runtime.InteropServices.ComVisible(true)]publicstaticDelegateCombine(paramsDelegate[]delegates){if(delegates==null||delegates.Length==0)returnnull;Delegated=delegates[0];for(inti=1;i<delegates.Length;i++)d=Combine(d,delegates[i]);returnd;}
publicstaticDelegateCombine(Delegatea,Delegateb){if((Object)a==null)returnb;returna.CombineImpl(b);}
以上两个方法为System.Delegate类型中,CombineImpl方法在MulticastDelegate重写。
[System.Security.SecuritySafeCritical]protectedoverridesealedDelegateCombineImpl(Delegatefollow){if((Object)follow==null)returnthis;if(!InternalEqualTypes(this,follow))thrownewArgumentException(Environment.GetResourceString("Arg_DlgtTypeMis"));MulticastDelegatedFollow=(MulticastDelegate)follow;Object[]resultList;intfollowCount=1;Object[]followList=dFollow._invocationListasObject[];if(followList!=null)followCount=(int)dFollow._invocationCount;intresultCount;Object[]invocationList=_invocationListasObject[];if(invocationList==null){resultCount=1+followCount;resultList=newObject[resultCount];resultList[0]=this;if(followList==null){resultList[1]=dFollow;}else{for(inti=0;i<followCount;i++)resultList[1+i]=followList[i];}returnNewMulticastDelegate(resultList,resultCount);}else{intinvocationCount=(int)_invocationCount;resultCount=invocationCount+followCount;resultList=null;if(resultCount<=invocationList.Length){resultList=invocationList;if(followList==null){if(!TrySetSlot(resultList,invocationCount,dFollow))resultList=null;}else{for(inti=0;i<followCount;i++){if(!TrySetSlot(resultList,invocationCount+i,followList[i])){resultList=null;break;}}}}if(resultList==null){intallocCount=invocationList.Length;while(allocCount<resultCount)allocCount*=2;resultList=newObject[allocCount];for(inti=0;i<invocationCount;i++)resultList[i]=invocationList[i];if(followList==null){resultList[invocationCount]=dFollow;}else{for(inti=0;i<followCount;i++)resultList[invocationCount+i]=followList[i];}}returnNewMulticastDelegate(resultList,resultCount,true);}}
再来具体看一下Remove()方法的底层实现代码,RemoveAll和Remove两个方法为System.Delegate类型中,CombineImpl方法在MulticastDelegate重写。
publicstaticDelegateRemoveAll(Delegatesource,Delegatevalue){DelegatenewDelegate=null;do{newDelegate=source;source=Remove(source,value);}while(newDelegate!=source);returnnewDelegate;}
[System.Security.SecuritySafeCritical]publicstaticDelegateRemove(Delegatesource,Delegatevalue){if(source==null)returnnull;if(value==null)returnsource;if(!InternalEqualTypes(source,value))thrownewArgumentException(Environment.GetResourceString("Arg_DlgtTypeMis"));returnsource.RemoveImpl(value);}
[System.Security.SecuritySafeCritical]protectedoverridesealedDelegateRemoveImpl(Delegatevalue){MulticastDelegatev=valueasMulticastDelegate;if(v==null)returnthis;if(v._invocationListasObject[]==null){Object[]invocationList=_invocationListasObject[];if(invocationList==null){if(this.Equals(value))returnnull;}else{intinvocationCount=(int)_invocationCount;for(inti=invocationCount;--i>=0;){if(value.Equals(invocationList[i])){if(invocationCount==2){return(Delegate)invocationList[1-i];}else{Object[]list=DeleteFromInvocationList(invocationList,invocationCount,i,1);returnNewMulticastDelegate(list,invocationCount-1,true);}}}}}else{Object[]invocationList=_invocationListasObject[];if(invocationList!=null){intinvocationCount=(int)_invocationCount;intvInvocationCount=(int)v._invocationCount;for(inti=invocationCount-vInvocationCount;i>=0;i--){if(EqualInvocationLists(invocationList,v._invocationListasObject[],i,vInvocationCount)){if(invocationCount-vInvocationCount==0){returnnull;}elseif(invocationCount-vInvocationCount==1){return(Delegate)invocationList[i!=0?0:invocationCount-1];}else{Object[]list=DeleteFromInvocationList(invocationList,invocationCount,i,vInvocationCount);returnNewMulticastDelegate(list,invocationCount-vInvocationCount,true);}}}}}returnthis;}
在以上的代码中,我们了解到了在.NET底层是如何实现委托实例的绑定和删除绑定。
在调用委托实例时,所有的操作都是顺序执行的。如果调用具有一个非void的返回类型,则调用的返回值是最后一个操作的返回值。如果调用列表中任何操作抛出异常,都会阻止执行后续的操作。
在上面提到了委托列表中出现非void实例调用,如果委托实例中出现多个非void调用,并且需要获取所有的委托实例的返回值结果,那么应该如何操作,在.NET红提供了一个方法GetInvocationList(),用于获取委托链表。
接下来具体了解一下GetInvocationList()的底层代码:
[System.Security.SecuritySafeCritical]publicoverridesealedDelegate[]GetInvocationList(){Delegate[]del;Object[]invocationList=_invocationListasObject[];if(invocationList==null){del=newDelegate[1];del[0]=this;}else{intinvocationCount=(int)_invocationCount;del=newDelegate[invocationCount];for(inti=0;i<invocationCount;i++)del[i]=(Delegate)invocationList[i];}returndel;}
当获取到委托实例列表后,可采用循环迭代的方式,依次获取每个委托实例的返回值。
再来了解一个属性Method,具体看一下此属性的底层实现代码:
publicMethodInfoMethod{get{returnGetMethodImpl();}}[System.Security.SecuritySafeCritical]protectedvirtualMethodInfoGetMethodImpl(){if((_methodBase==null)||!(_methodBaseisMethodInfo)){IRuntimeMethodInfomethod=FindMethodHandle();RuntimeTypedeclaringType=RuntimeMethodHandle.GetDeclaringType(method);if(RuntimeTypeHandle.IsGenericTypeDefinition(declaringType)||RuntimeTypeHandle.HasInstantiation(declaringType)){boolisStatic=(RuntimeMethodHandle.GetAttributes(method)&MethodAttributes.Static)!=(MethodAttributes)0;if(!isStatic){if(_methodPtrAux==(IntPtr)0){TypecurrentType=_target.GetType();TypetargetType=declaringType.GetGenericTypeDefinition();while(currentType!=null){if(currentType.IsGenericType&¤tType.GetGenericTypeDefinition()==targetType){declaringType=currentTypeasRuntimeType;break;}currentType=currentType.BaseType;}BCLDebug.Assert(currentType!=null||_target.GetType().IsCOMObject,"Theclasshierarchyshoulddeclarethemethod");}else{MethodInfoinvoke=this.GetType().GetMethod("Invoke");declaringType=(RuntimeType)invoke.GetParameters()[0].ParameterType;}}}_methodBase=(MethodInfo)RuntimeType.GetMethodBase(declaringType,method);}return(MethodInfo)_methodBase;}
以上是System.Delegate类中的定义,接下来看一下MulticastDelegate重写:
[System.Security.SecuritySafeCritical]protectedoverrideMethodInfoGetMethodImpl(){if(_invocationCount!=(IntPtr)0&&_invocationList!=null){Object[]invocationList=_invocationListasObject[];if(invocationList!=null){intindex=(int)_invocationCount-1;return((Delegate)invocationList[index]).Method;}MulticastDelegateinnerDelegate=_invocationListasMulticastDelegate;if(innerDelegate!=null){returninnerDelegate.GetMethodImpl();}}elseif(IsUnmanagedFunctionPtr()){if((_methodBase==null)||!(_methodBaseisMethodInfo)){IRuntimeMethodInfomethod=FindMethodHandle();RuntimeTypedeclaringType=RuntimeMethodHandle.GetDeclaringType(method);if(RuntimeTypeHandle.IsGenericTypeDefinition(declaringType)||RuntimeTypeHandle.HasInstantiation(declaringType)){RuntimeTypereflectedType=GetType()asRuntimeType;declaringType=reflectedType;}_methodBase=(MethodInfo)RuntimeType.GetMethodBase(declaringType,method);}return(MethodInfo)_methodBase;}returnbase.GetMethodImpl();}
以上是对委托的相关定义,以及有关委托的一些操作方法的说明,没有具体指出如何去创建和使用委托,因为委托的简单创建和一般应用,对于大部分开发者来说是相对较为简单的,因为微软在不断的对C#的语法进行提升和修改,极大的简化了对应的操作。但是正是由于在应用层做了较大的封装,这也会导致特性在底层的复杂度慢慢的增大。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。