博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
委托链
阅读量:5168 次
发布时间:2019-06-13

本文共 3620 字,大约阅读时间需要 12 分钟。

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方法删除了)

转载于:https://www.cnblogs.com/polly333/p/4498398.html

你可能感兴趣的文章
洛谷P2692 覆盖 题解
查看>>
Linux下清理内存和Cache方法见下文:
查看>>
【AngularJs-模块篇-Form篇】
查看>>
支持向量基
查看>>
单链表 类
查看>>
类的组合 构造函数的用法
查看>>
ORACLE SQL:经典查询练手第三篇
查看>>
ubuntu 包管理
查看>>
java -io字符流FileWrite操作演示
查看>>
vue回到上一个位置
查看>>
UESTC_Infected Land 2015 UESTC Training for Search Algorithm & String<Problem G>
查看>>
.Net 之 RPC 框架之Hprose(远程调用对象)
查看>>
全球外贸客户资源网站总汇
查看>>
杂项-CORS:CORS(跨域资源共享)
查看>>
杨柳目-杨柳科:杨柳科
查看>>
Node.js:JXcore
查看>>
oracle数据投毒,Oracle Database Server TNS Listener远程数据投毒漏洞(CVE-2012-1675)的完美解决方法...
查看>>
oracle创建函数难点,oracle创建函数遇到的坑,
查看>>
PHP抽象函数的依赖注入,依赖注入_PHP编程_互联网开发技术网_传播最新的编程技术_php361.com...
查看>>
linux下创建nginx虚拟目录详解,配置Nginx服务器虚拟目录
查看>>