博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
《CLR Via C# 第3版》笔记之(二十四) - 委托
阅读量:7079 次
发布时间:2019-06-28

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

委托是.net中实现回调机制的一种重要技术,尤其在编写服务端程序的时候,更是频繁使用委托。

主要内容:

  • 委托本质
  • 委托链
  • 动态委托

 

1. 委托本质

委托本质其实就是一个类,基本上在可以定义类的地方都可以定义委托,C#中委托的写法其实只是C#的语法糖。

如下定义个委托:

using System;namespace delegate_test{    internal delegate int Sum(int a, int b);}

用ILSpy可以发现对应的IL代码如下:

.class private auto ansi sealed delegate_test.Sum    extends [mscorlib]System.MulticastDelegate{    // Methods    .method public hidebysig specialname rtspecialname         instance void .ctor (            object 'object',            native int 'method'        ) runtime managed     {    } // end of method Sum::.ctor    .method public hidebysig newslot virtual         instance int32 Invoke (            int32 a,            int32 b        ) runtime managed     {    } // end of method Sum::Invoke    .method public hidebysig newslot virtual         instance class [mscorlib]System.IAsyncResult BeginInvoke (            int32 a,            int32 b,            class [mscorlib]System.AsyncCallback callback,            object 'object'        ) runtime managed     {    } // end of method Sum::BeginInvoke    .method public hidebysig newslot virtual         instance int32 EndInvoke (            class [mscorlib]System.IAsyncResult result        ) runtime managed     {    } // end of method Sum::EndInvoke} // end of class delegate_test.Sum

从中我们可以看出

  1. 委托是一个类,继承于System.MulticastDelegate。
  2. 委托类中有3个方法和一个构造函数
  3. 委托构造函数中需传入2个参数

 

为了更好的了解委托的类,下面写了个简单的例子先来了解一下委托的构造函数:

using System;namespace delegate_test{    internal delegate int Sum(int a, int b);        class Program    {        public static void Main(string[] args)        {            Console.WriteLine("Hello World!");                        // 实例化委托,这里和委托的IL代码中构造函数不一致            // IL代码中 Sum 的构造函数需要传入2个参数(一个object 类型,一个 native int类型)            /*            .method public hidebysig specialname rtspecialname                 instance void .ctor (                        object 'object',                        native int 'method'                    ) runtime managed                 {                } // end of method Sum::.ctor            */            Sum s = new Sum(Add);                        Console.Write("Press any key to continue . . . ");            Console.ReadKey(true);        }                public static int Add(int a, int b)        {            return a+b;        }    }}

注意代码的注释,我们看出C#中实例化委托的写法与IL中看到的委托的构造函数不一致,

其实这只是C#的语法糖,为了方便代码编写,将上面的代码编译后,用ILSpy查看IL代码:

IL_0000: nop    IL_0001: ldstr "Hello World!"    IL_0006: call void [mscorlib]System.Console::WriteLine(string)    IL_000b: nop    IL_000c: ldnull    IL_000d: ldftn int32 delegate_test.Program::Add(int32, int32)    IL_0013: newobj instance void delegate_test.Sum::.ctor(object, native int)

我们发现,实际运行时,还是构造了2个参数传给了委托的构造函数,一个null,还有一个是函数Add的地址。

这里的第一个参数object为null的原因是,Add函数是static,不用实例化一个object来调用它。

下面构造一个非static的函数Add2来看看object的值是否还为null。

using System;namespace delegate_test{    internal delegate int Sum(int a, int b);        class Program    {        public static void Main(string[] args)        {            Console.WriteLine("Hello World!");                        // 实例化委托,这里和委托的IL代码中构造函数不一致            // IL代码中 Sum 的构造函数需要传入2个参数(一个object 类型,一个 native int类型)            /*            .method public hidebysig specialname rtspecialname                 instance void .ctor (                        object 'object',                        native int 'method'                    ) runtime managed                 {                } // end of method Sum::.ctor            */            Sum s = new Sum(Add);            // 非static函数 Add2            Sum s2 = new Sum((new Program()).Add2);                        Console.Write("Press any key to continue . . . ");            Console.ReadKey(true);        }                public static int Add(int a, int b)        {            return a+b;        }                public int Add2(int a, int b)        {            return a+b;        }    }}

Sum s2 = new Sum((new Program()).Add2); 这句对应的IL如下:

IL_0019: newobj instance void delegate_test.Program::.ctor()    IL_001e: ldftn instance int32 delegate_test.Program::Add2(int32, int32)    IL_0024: newobj instance void delegate_test.Sum::.ctor(object, native int)

 

委托的Invoke函数就是我们实际运行委托时调用的函数,至于BeginInvoke和EndInvoke则是用于异步的情况。

using System;namespace delegate_test{    internal delegate int Sum(int a, int b);        class Program    {        public static void Main(string[] args)        {            Console.WriteLine("Hello World!");                        // 实例化委托,这里和委托的IL代码中构造函数不一致            // IL代码中 Sum 的构造函数需要传入2个参数(一个object 类型,一个 native int类型)            /*            .method public hidebysig specialname rtspecialname                 instance void .ctor (                        object 'object',                        native int 'method'                    ) runtime managed                 {                } // end of method Sum::.ctor            */            Sum s = new Sum(Add);                        Sum s2 = new Sum((new Program()).Add2);                        // 调用委托的代码 s(5,7) 其实就是调用委托类的 Invoke方法            Console.WriteLine("5+7="+ s(5,7));            // 剥去语法外衣,也可以写成            Console.WriteLine("5+7="+ s.Invoke(5,7));                        Console.Write("Press any key to continue . . . ");            Console.ReadKey(true);        }                public static int Add(int a, int b)        {            return a+b;        }                public int Add2(int a, int b)        {            return a+b;        }    }}

s(5,7) 就是 s.Invoke(5,7) 的简化写法。

 

2.  委托链

委托既然是用于回调函数,那就应该可以一次回调多个函数,形成一个委托链。

这个委托链中应该还可以动态追加和减少回调函数。

实验代码如下:

using System;namespace delegate_test{    internal delegate void Print_Sum(int a, int b);        class Program    {        public static void Main(string[] args)        {            Console.WriteLine("Hello World!");                     Print_Sum s = null;                        Print_Sum s1 = new Print_Sum(Add1);                        Print_Sum s2 = new Print_Sum((new Program()).Add2);            Print_Sum s3 = new Print_Sum((new Program()).Add3);                        s += s1;            s += s2;            s += s3;                        s(1,2);                        // 委托链中删除Add2            s -= s2;            s(1,2);                        Console.Write("Press any key to continue . . . ");            Console.ReadKey(true);        }                public static void Add1(int a, int b)        {            Console.WriteLine("function Add1.... result is " + (a+b).ToString());        }                public void Add2(int a, int b)        {            Console.WriteLine("function Add2.... result is " + (2*(a+b)).ToString());        }                public void Add3(int a, int b)        {            Console.WriteLine("function Add3.... result is " + (3*(a+b)).ToString());        }    }}

运行的结果也预期相符,其实其中的 s += s1;s -= s2; 也是C#的语法糖,实际调用的是System.DelegateCombine方法和Remove方法。

感兴趣的话,可以看编译后的IL代码。

 

3. 动态委托

动态委托其实应用的并不多,它其实是和反射结合使用的。只有在你无法确定所调用的委托所定义的参数个数和参数类型时,才需要用到动态委托。

使用动态委托主要就是使用 生成委托实例的 CreateDelegate 方法和 调用委托的 DynamicInvoke 方法。

using System;using System.Reflection;internal delegate void print_sum(int a, int b);internal delegate void print_string(string a);public class Dynamic_Delegate{    public static void Main(string[] args)    {        // 假设要调用的delegate名称,方法名称,方法的参数都是由 args传入的。        // Main方法中并不知道要调用哪个委托        // 调用委托print_sum时:        // delegate_test.exe print_sum Sum 2 3        // 调用委托print_string时:        // delegate_test.exe print_string Str "hello delegate!"                // 获取各个参数        string delegate_name = args[0];        string method_name = args[1];        // 由于2种委托的参数不同,所以参数可能是一个,可能是两个        object[] method_args = new object[args.Length - 2];        for (int i = 0; i

根据编译成的程序集(我的程序集名称是delegate_test.exe),在命令行中分别输入如下命令来分别调用不同的delegate:

delegate_test.exe print_sum Sum 2 3delegate_test.exe print_string Str "hello delegate!"

转载地址:http://zbpml.baihongyu.com/

你可能感兴趣的文章
MySQL基础之---mysqlimport工具和LOAD DATA命令导入文本文件
查看>>
php 读取文件头部两个字节 判断文件的实际类型
查看>>
异或交换真的比开一个tmp快吗?
查看>>
使用sea.js管理你项目js文件
查看>>
windows device driver 小结感想
查看>>
SQLServer获取临时表列名并判断指定列名是否存在
查看>>
4827 妹子[快速乘法]
查看>>
Ubuntu的一些使用记录
查看>>
DataBase Connection Failed的一点解决办法(PHP项目)
查看>>
SilverLight控件之ContextMenu和RadContextMenu(菜单)
查看>>
css3背景颜色渐变属性 兼容性测试基础环境为:windows系统;IE6.0+, Firefox4.0+, Chrome4.0+, Safari4.0+, Opera15.0+...
查看>>
word怎么删除空白页
查看>>
2017 计蒜之道 初赛 第五场 A. UCloud 机房的网络搭建
查看>>
探索SpringBoot中的SpringMVC
查看>>
memcpy的用法总结
查看>>
HDU 4027 Can you answer these queries?
查看>>
jq购物车结算功能
查看>>
leetcode725
查看>>
Android WebRTC 音视频开发总结(三)-- 信令服务和媒体服务
查看>>
EntityFramework IEnumerable,IQueryable ,Include
查看>>