委托链

1、委托的本质

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

你可能感兴趣的:(委托链)