zoukankan      html  css  js  c++  java
  • 委托链

    1、委托的本质

    在调用委托我们是定义-》new实例,很让人以为是调用一个方法。实际上委托delegate就是一个类,类继承于FCL中定义的Systme.MulticastDelegate类型,所有委托类型都派生于MulticastDelegate,该类中还定义了四个方法,一个构造函数,Invoke方法,还有就是两个异步方法BeginInvoke和EndInvoke方法。委托可以理解为方法的“外号”。我们在IDE中定义一个委托类型时,最终是通过编译器将定义的代码转化为中间语言IL,然后再执行中间语言中的代码来转化为本机代码的,所以在Visual Studio中编写的代码只是一个包装而已,真真程序执行的是中间语言中的代码的。
    由于所有委托类型都是继承于MulticastDelegate,MulticastDelegate又继承于Delegate,所以委托类型继承了MulticastDelegate的字段、属性和方法,在这些成员中,有三个非公共字段与后面专题要介绍的委托链有关,所以在这里先列出来的。
    这里写图片描述
    在程序中静态实例化和方法实例化不太一样。
    这里写图片描述
    其中第一个_Target静态实例化是null。方式实例化,那是个对象。记住第三个参数invocationList是个委托数组,用于存储委托,下面就讲。

    2、委托数组

    委托链也是一个委托,只是因为它是把多个委托链在一起,所以我们就以委托链来这么称呼它的。
    每次调用委托链时,委托链包装的每个方法都会顺序被执行,如果委托链中被调用的委托抛出一个异常,这样链中的后续所有对象都不能被调用,并且如果委托的前面具有一个非void的返回类型,则只有最后一个返回值会被保留,其他所有回调方法的返回值都会被舍弃,这就意味着其他所有操作的返回值都永远看不到的吗? 事实却不是这样的,我们可以通过调用Delegate.GetInvocationList方法来显式调用链中的每一个委托,同时可以添加一些自己的定义输出。
    GetInvocationList方法返回一个由Delegate引用构成的数组,其中每一个数组都指向链中的一个委托对象。在内部,GetInvocationList创建并初始化一个数组,让数据的每一个元素都引用链中的一个委托,然后返回对该数组的一个引用。如果_invocatinList字段为null,返回的数组只有一个元素,该元素就是委托实例本身。下面就通过一个程序来演示下的:

    namespace DelegateChainDemo
    {
        class Program
        {
            // 声明一个委托类型,它的实例引用一个方法
            // 该方法回去一个int 参数,返回void类型
            public delegate string DelegateTest();
    
            static void Main(string[] args)
            {
                // 用静态方法来实例化委托
                DelegateTest dtstatic = new DelegateTest(Program.method1);
    
                // 用实例方法来实例化委托
                DelegateTest dtinstance = new DelegateTest(new Program().method2);
                DelegateTest dtinstance2 = new DelegateTest(new Program().method3);
                // 定义一个委托链对象,一开始初始化为null,就是不代表任何方法(我就是我,我不代表任何人)
                DelegateTest delegatechain = null;
                delegatechain += dtstatic;
                delegatechain += dtinstance;
                delegatechain += dtinstance2;
    
                ////delegatechain =(DelegateTest)Delegate.Remove(delegatechain,new DelegateTest(method1));
                ////delegatechain = (DelegateTest)Delegate.Remove(delegatechain, new DelegateTest(new Program().method2));
                Console.WriteLine(Test(delegatechain));
                Console.Read();
            }
    
            private static string method1()
            {
                return "这是静态方法1";
            }
    
            private string method2()
            {
                throw new Exception("抛出了一个异常");
            }
    
            private string method3()
            {
                return "这是实例方法3";
            }
            // 测试调用委托的方法
            private static string Test(DelegateTest chain)
            {
                if (chain == null)
                {
                    return null;
                }
    
                // 用这个变量来保存输出的字符串
                StringBuilder returnstring = new StringBuilder();
    
                // 获取一个委托数组,其中每个元素都引用链中的委托
                Delegate[] delegatearray=chain.GetInvocationList();
    
                // 遍历数组中的每个委托
                foreach (DelegateTest t in delegatearray)
                {
                    try
                    {
                        //调用委托获得返回值
                        returnstring.Append(t() + Environment.NewLine);
                    }
                    catch (Exception e)
                    {
                        returnstring.AppendFormat("异常从 {0} 方法中抛出, 异常信息为:{1}{2}", t.Method.Name, e.Message, Environment.NewLine);
                    }
                }
    
                // 把结果返回给调用者
                return returnstring.ToString();
            }
        }
    }

    Remove方法被调用时,它会扫描delegateChain(第一个参数)所引用的委托对象内部维护的委托数组(如果对于委托数组为空的情况下调用Remove方法将不会有任何作用,就是不会删除任何委托引用,这里主要是说明扫描是从委托数组里进行扫描),如果找到delegateChain引用的委托对象的_target和_methodPtr字段

    和第二个参数(新创建的委托)中的字段匹配的委托,如果删除之后数组中只剩下一个数据项时,就返回那个数据项(而不会去新建一个委托对象再初始化的,此时的_invocationList为null,而不是保存一个委托对象引用的数组了,具体可以Remove一个后调试看看的),如果此时数组中还剩余多个数据项,就新建一个委托对象——其中创建并初始化_invocationList数组(此时的数组引用的委托对象已经少了一个了,因为用Remove方法删除了)

  • 相关阅读:
    Codeforces round 493 Convert to Ones
    石子合并系列问题【区间dp,环形,四边不等式优化】
    UVa 10635
    选课【树形dp】
    JSOI2016病毒感染
    加分二叉树【树形dp】
    人为什么活着__稻盛和夫的哲学
    213. House Robber II
    安装 error: Microsoft Visual C++ 14.0 is required 解决方案
    ImportError:no mudle named 'cv2'
  • 原文地址:https://www.cnblogs.com/polly333/p/4498398.html
Copyright © 2011-2022 走看看