zoukankan      html  css  js  c++  java
  • 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)] 
            public static Delegate Combine(params Delegate[] delegates) 
            {
                if (delegates == null || delegates.Length == 0) 
                    return null;
    
                Delegate d = delegates[0];
                for (int i = 1; i < delegates.Length; i++) 
                    d = Combine(d,delegates[i]);
     
                return d; 
            }
    public static Delegate Combine(Delegate a, Delegate b) 
            {
                if ((Object)a == null) 
                    return b;
    
                return  a.CombineImpl(b);
            } 

        以上两个方法为System.Delegate类型中,CombineImpl方法在MulticastDelegate重写。

            [System.Security.SecuritySafeCritical]  
            protected override sealed Delegate CombineImpl(Delegate follow)
            { 
                if ((Object)follow == null) 
                    return this;
                if (!InternalEqualTypes(this, follow))
                    throw new ArgumentException(Environment.GetResourceString("Arg_DlgtTypeMis"));
     
                MulticastDelegate dFollow = (MulticastDelegate)follow;
                Object[] resultList; 
                int followCount = 1; 
                Object[] followList = dFollow._invocationList as Object[];
                if (followList != null) 
                    followCount = (int)dFollow._invocationCount;
    
                int resultCount;
                Object[] invocationList = _invocationList as Object[]; 
                if (invocationList == null)
                { 
                    resultCount = 1 + followCount; 
                    resultList = new Object[resultCount];
                    resultList[0] = this; 
                    if (followList == null)
                    {
                        resultList[1] = dFollow;
                    } 
                    else
                    { 
                        for (int i = 0; i < followCount; i++) 
                            resultList[1 + i] = followList[i];
                    } 
                    return NewMulticastDelegate(resultList, resultCount);
                }
                else
                { 
                    int invocationCount = (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 (int i = 0; i < followCount; i++) 
                            {
                                if (!TrySetSlot(resultList, invocationCount + i, followList[i]))
                                {
                                    resultList = null; 
                                    break;
                                } 
                            } 
                        }
                    } 
                    if (resultList == null)
                    {
                        int allocCount = invocationList.Length; 
                        while (allocCount < resultCount)
                            allocCount *= 2; 
     
                        resultList = new Object[allocCount];
     
                        for (int i = 0; i < invocationCount; i++)
                            resultList[i] = invocationList[i];
    
                        if (followList == null) 
                        {
                            resultList[invocationCount] = dFollow; 
                        } 
                        else
                        { 
                            for (int i = 0; i < followCount; i++)
                                resultList[invocationCount + i] = followList[i];
                        }
                    } 
                    return NewMulticastDelegate(resultList, resultCount, true);
                } 
            } 

       再来具体看一下Remove()方法的底层实现代码,RemoveAll和Remove两个方法为System.Delegate类型中,CombineImpl方法在MulticastDelegate重写。:

     public static Delegate RemoveAll(Delegate source, Delegate value) 
            {
                Delegate newDelegate = null; 
    
                do
                {
                    newDelegate = source; 
                    source = Remove(source, value);
                } 
                while (newDelegate != source); 
    
                return newDelegate; 
            }
    [System.Security.SecuritySafeCritical] 
            public static Delegate Remove(Delegate source, Delegate value)
            {
                if (source == null) 
                    return null;
     
                if (value == null) 
                    return source;
     
                if (!InternalEqualTypes(source, value))
                    throw new ArgumentException(Environment.GetResourceString("Arg_DlgtTypeMis"));
    
                return source.RemoveImpl(value); 
            }

            [System.Security.SecuritySafeCritical] 
            protected override sealed Delegate RemoveImpl(Delegate value)
            {             MulticastDelegate v = value as MulticastDelegate; 
    
                if (v == null) 
                    return this; 
                if (v._invocationList as Object[] == null)
                { 
                    Object[] invocationList = _invocationList as Object[];
                    if (invocationList == null)
                    {
                        if (this.Equals(value))
                            return null; 
                    } 
                    else
                    { 
                        int invocationCount = (int)_invocationCount;
                        for (int i = invocationCount; --i >= 0; )
                        {
                            if (value.Equals(invocationList[i])) 
                            {
                                if (invocationCount == 2) 
                                { 
                                    return (Delegate)invocationList[1-i]; 
                                }
                                else
                                {
                                    Object[] list = DeleteFromInvocationList(invocationList, invocationCount, i, 1); 
                                    return NewMulticastDelegate(list, invocationCount-1, true);
                                } 
                            } 
                        }
                    } 
                }
                else
                {
                    Object[] invocationList = _invocationList as Object[]; 
                    if (invocationList != null) {
                        int invocationCount = (int)_invocationCount; 
                        int vInvocationCount = (int)v._invocationCount; 
                        for (int i = invocationCount - vInvocationCount; i >= 0; i--)
                        { 
                            if (EqualInvocationLists(invocationList, v._invocationList as Object[], i, vInvocationCount))
                            {
                                if (invocationCount - vInvocationCount == 0)
                                { 
                                    return null; 
                                } 
                                else if (invocationCount - vInvocationCount == 1)
                                { 
                                    return (Delegate)invocationList[i != 0 ? 0 : invocationCount-1];
                                }
                                else 
                                {
                                    Object[] list = DeleteFromInvocationList(invocationList, invocationCount, i, vInvocationCount); 
                                    return NewMulticastDelegate(list, invocationCount - vInvocationCount, true); 
                                }
                            } 
                        }
                    }
                }
     
                return this;
            } 

      在以上的代码中,我们了解到了在.NET底层是如何实现委托实例的绑定和删除绑定。

      在调用委托实例时,所有的操作都是顺序执行的。如果调用具有一个非void的返回类型,则调用的返回值是最后一个操作的返回值。如果调用列表中任何操作抛出异常,都会阻止执行后续的操作。

       在上面提到了委托列表中出现非void实例调用,如果委托实例中出现多个非void调用,并且需要获取所有的委托实例的返回值结果,那么应该如何操作,在.NET红提供了一个方法GetInvocationList(),用于获取委托链表。

      接下来具体了解一下GetInvocationList()的底层代码:

          [System.Security.SecuritySafeCritical] 
            public override sealed Delegate[] GetInvocationList()
            {
                Delegate[] del;
                Object[] invocationList = _invocationList as Object[];
                if (invocationList == null)
                { 
                    del = new Delegate[1];
                    del[0] = this; 
                } 
                else
                { 
                    int invocationCount = (int)_invocationCount;
                    del = new Delegate[invocationCount]; 
    
                    for (int i = 0; i < invocationCount; i++) 
                        del[i] = (Delegate)invocationList[i]; 
                }
                return del; 
            }

       当获取到委托实例列表后,可采用循环迭代的方式,依次获取每个委托实例的返回值。

       再来了解一个属性Method,具体看一下此属性的底层实现代码:

           public MethodInfo Method 
            {
                get
                {
                    return GetMethodImpl(); 
                }
            } 
     
            [System.Security.SecuritySafeCritical] 
            protected virtual MethodInfo GetMethodImpl() 
            {
                if ((_methodBase == null) || !(_methodBase is MethodInfo))
                {
                    IRuntimeMethodInfo method = FindMethodHandle(); 
                    RuntimeType declaringType = RuntimeMethodHandle.GetDeclaringType(method);
                    if (RuntimeTypeHandle.IsGenericTypeDefinition(declaringType) || RuntimeTypeHandle.HasInstantiation(declaringType)) 
                    {
                        bool isStatic = (RuntimeMethodHandle.GetAttributes(method) & MethodAttributes.Static) != (MethodAttributes)0; 
                        if (!isStatic)
                        {
                            if (_methodPtrAux == (IntPtr)0)
                            { 
                                Type currentType = _target.GetType(); 
                                Type targetType = declaringType.GetGenericTypeDefinition(); 
                                while (currentType != null)
                                { 
                                    if (currentType.IsGenericType &&
                                        currentType.GetGenericTypeDefinition() == targetType)
                                    {
                                        declaringType = currentType as RuntimeType; 
                                        break;
                                    } 
                                    currentType = currentType.BaseType; 
                                }
    
                                BCLDebug.Assert(currentType != null || _target.GetType().IsCOMObject, "The class hierarchy should declare the method"); 
                            }
                            else 
                            { 
                                MethodInfo invoke = 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] 
            protected override MethodInfo GetMethodImpl()
            { 
                if (_invocationCount != (IntPtr)0 && _invocationList != null) 
                {
                    Object[] invocationList = _invocationList as Object[];
                    if (invocationList != null)
                    {
                        int index = (int)_invocationCount - 1; 
                        return ((Delegate)invocationList[index]).Method;
                    } 
                    MulticastDelegate innerDelegate = _invocationList as MulticastDelegate; 
                    if (innerDelegate != null)
                    { 
                        return innerDelegate.GetMethodImpl();
                    }
                } 
                else if (IsUnmanagedFunctionPtr())
                { 
                    if ((_methodBase == null) || !(_methodBase is MethodInfo)) 
                    {
                        IRuntimeMethodInfo method = FindMethodHandle();
                        RuntimeType declaringType = RuntimeMethodHandle.GetDeclaringType(method);
                        if (RuntimeTypeHandle.IsGenericTypeDefinition(declaringType) || RuntimeTypeHandle.HasInstantiation(declaringType)) 
                        { 
                            RuntimeType reflectedType = GetType() as RuntimeType; 
                            declaringType = reflectedType;
                        }
                        _methodBase = (MethodInfo)RuntimeType.GetMethodBase(declaringType, method);
                    } 
                    return (MethodInfo)_methodBase;
                } 
                return base.GetMethodImpl(); 
            }

       以上是对委托的相关定义,以及有关委托的一些操作方法的说明,没有具体指出如何去创建和使用委托,因为委托的简单创建和一般应用,对于大部分开发者来说是相对较为简单的,因为微软在不断的对C#的语法进行提升和修改,极大的简化了对应的操作。但是正是由于在应用层做了较大的封装,这也会导致特性在底层的复杂度慢慢的增大。

  • 相关阅读:
    C#:新邮件监听及搜索
    PHPexcel导入数据的时候出现object解决方法
    selectpage选择订单的时候,订单数量和金额会动态改变
    三、变量的简述
    TP框架where条件和whereOr条件同时使用
    一.OS运行机制
    二.进制简述
    1.go语言入门
    C# Redis学习系列二:Redis基本设置
    C# Redis学习系列一:Redis的认识、下载、安装、使用
  • 原文地址:https://www.cnblogs.com/pengze0902/p/6088870.html
Copyright © 2011-2022 走看看