快速理解C#高级概念(一) Delegate委托

做.NET开发很久,最近重新温习《C#高级编程》一书。发现很多曾经似懂非懂的问题,其实也是能够慢慢钻研慢慢理解的。

所以,打算开写《C#高级编程系列》博文。其中会借鉴《C#高级编程》一书的概念,也会参照其他高手的博文,希望大家谅解。有不对的地方,欢迎指正。

(另:本博文不会讲解定义,语法方面的基础知识。)

下面如题,我们来讲委托。

Delegate委托,在.NET中应用的非常广泛。会涉及到Lambda表达式,事件,匿名方法等(请关注后续博文)。

那么何为委托?

通俗的来讲,委托除了必须指定delegate关键字和没有方法实体之外,和指定方法没有更多区别。你可以当它是一个占位符,比如你在写代码的时候并不知道你将要处理的是什么。你只需要知道你将要引入的参数类型和输出类型是什么并定义它即可。这就是书本上所传达的方法签名必须相同的意思。

 

下面我们来定义一个基本的委托:

 1     public class Test
 2     {
 3         //定义委托
 4         public delegate void D_Math(int a, int b);
 5         public void Add(int a, int b)
 6         {
 7             Console.WriteLine("Add方法结果:{0}", a + b);
 8         }
 9         public void Cut(int a, int b)
10         {
11             Console.WriteLine("Cut方法结果:{0}", a - b);
12         }
13     }
14     [TestClass]
15     public class UnitTest1
16     {
17         [TestMethod]
18         public void TestMethod1()
19         {
20             Test t = new Test();
21             Test.D_Math D = new Test.D_Math(t.Add);//委托实例化,也可Test.D_Math D =t.Add;
22             D += t.Cut;//委托可以以队列方式执行多个方法,以+=运算符或者-=来增加或者取消队列中的方法
23             D(5, 6);
24
25         }
26     }

以上看出来委托实用的地方了吗?即委托可以执行任何引入参数类型相同且返回类型相同的方法,甚至可以执行签名相同的方法队列。

那么我们的方法签名(即引入参数和输出参数)真的必须与委托完全一致吗?答:不是的,我们不能忽略协变与逆变。

我们这里简单介绍一下协变与逆变的知识。

“协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型。

“逆变”则是指能够使用派生程度更小的类型。

那么,我们的委托也是接受协变与逆变的。

意思是,如果定义一个delegate,那么不仅仅签名完全相同的方法可以赋值给delegate变量。

如果一个方法的参数表符合delegate声明,但返回的类型是(delegate声明返回类型)的派生类,那也可以将这个方法赋值给这个delegate变量。

如果一个方法的返回类型符合delegate的声明,但参数是(delegate声明参数类型)的祖先类,那也可以将这个方法赋值给这个delegate变量。

如果一个方法的参数和返回类型都符合上面两行的假设,那也可以将这个方法赋值给这个delegate变量。

以下以两个简单示例解释协变与逆变:

协变:

 1       public class A { }
 2     public class B:A { }//B继承自A
 3     public class Test
 4     {
 5         //定义委托
 6         public delegate A D_Math();
 7         public B Add()
 8         {
 9             return new B();
10  
11         }
12         public A Add2()
13         {
14             return new A();
15         }
16     }
17     [TestClass]
18     public class UnitTest1
19     {
20         [TestMethod]
21         public void TestMethod1()
22         {
23             Test.D_Math d = new Test.D_Math(new Test().Add);//委托返回A,而Add方法返回B,此为协变。
24         }
25     }

逆变:

 1  public class A { }
 2     public class B:A { }//B继承自A
 3     public class Test
 4     {
 5         //定义委托
 6         public delegate void D_Math(B b);
 7         public void Add(B b)
 8         {
 9
10         }
11         public void Add2(A a)
12         {
13
14         }
15     }
16     [TestClass]
17     public class UnitTest1
18     {
19         [TestMethod]
20         public void TestMethod1()
21         {
22             Test.D_Math d = new Test.D_Math(new Test().Add2);//委托引入参数B,而Add方法参数为A类型,此为协逆变。
23         }
24     }
时间: 2024-10-07 21:02:28

快速理解C#高级概念(一) Delegate委托的相关文章

快速理解VirtualBox的四种网络连接方式

VirtualBox中有4中网络连接方式: NAT Bridged Adapter Internal Host-only Adapter VMWare中有三种,其实他跟VMWare 的网络连接方式都是一样概念,只是比VMWare多了Internal方式. 要让自己(或别人)理解深刻,方法就是做比较和打比方,比较之间的不同和相同,拿熟知的事物打比方.先来一张图,通过这张图就很容易看出这4种方式的区别: (注:此图直接取至Finalbug的Blog,表示感谢) 再来用文字做详细的解释(其实归结起来就

理解 Keystone 核心概念 - 每天5分钟玩转 OpenStack(18)

作为 OpenStack 的基础支持服务,Keystone 做下面这几件事情: 管理用户及其权限 维护 OpenStack Services 的 Endpoint Authentication(认证)和 Authorization(鉴权) 学习 Keystone,得理解下面这些概念: User User 指代任何使用 OpenStack 的实体,可以是真正的用户,其他系统或者服务. 当 User 请求访问 OpenStack 时,Keystone 会对其进行验证. Horizon 在 Ident

如何快速理解一个全新的嵌入式操作系统

---基于TI CC254X OSAL的分析 当工具链配置完成后,Source Insight向你展示一份源码工程,不借助百度和开发文档,能否在一两个小时内理解源码的组成框架和接口,进行快速开发? 在笔者过往撰写的博文中,一直在倡导两个嵌入式学习和开发理念:提高嵌入式系统架构和软件层次形成大局观:掌握从需求的角度去理解新系统和技术这个方法论.在软件大局观作为学习新系统的背景知识的基础上,从软件需求的角度入手就能快速理解和掌握一个全新的系统.本文以TI蓝牙BLE CC254x的源码库和工程为例进行

[转帖]十分钟快速理解DPI和PPI,不再傻傻分不清!

十分钟快速理解DPI和PPI,不再傻傻分不清! https://baijiahao.baidu.com/s?id=1605834796518990333&wfr=spider&for=pc关于UX测试相关的: 之前一直搞的不是很清楚 这个百家号的解释挺好的 简单转帖一下 以后仔细研究,  设计师充电站 18-07-1308:58 72DPI的图片拿去打印会糊吗?手机拍出来的照片是多少DPI?PS里显示72PPI为什么另存为JPG就变成96DPI了? 类似的问题层出不穷.本站很多篇文章都讲到

深入理解WORD高级排版之模板与加载项

WORD中四大核心技术是样式.域.宏和模板.本文集中讨论模板使用中的有关"模板与加载项"方面的疑问. 一.模板技术 模板是一类特殊的Word文档,它提供了编辑文档的基本工具和文本格式.模板一般包含每个文档中都显示的文字和图形(页眉和页脚:插入日期和时间.文档标题等信息的域:占位符:公司徽标等).页面设置.样式.自定义工具栏.菜单和快捷键等元素.Word 2003的默认模板名为"空白文档"(公共模板).当建立一个新文档时,若没有选择其他类型的模板文件,Word就会将&

如何快速理解一个全新的嵌入式操作系统(续)

---基于TI CC254X OSAL的分析 当工具链配置完成后,SourceInsight向你展示一份源码工程,不借助百度和开发文档,能否在一两个小时内理解源码的组成框架和接口,进行快速开发? 上一篇<如何快速理解一个全新的嵌入式操作系统>我们已经分析了如何快速理解OSAL的任务调度和任务间通信(其实OSAL只是酷似多任务操作系统的单任务系统),再理解好OASL的消息产生和处理过程,我们就能够进行快速开发了. 一.消息的来源 嵌入式系统的消息包括两种,一是系统消息,包括低电.热插拔等,由系统

深入理解Java闭包概念

闭包又称词法闭包 闭包最早定义为一种包含<环境成分>和<控制成分>的实体. 解释一:闭包是引用了自由变量的函数,这个被引用的变量将和这个函数一同存在. 解释二:闭包是函数和相关引用环境组成的实体. 注:<自由变量>:除了局部变量的其他变量 简单理解:闭包能够将一个方法作为一个变量去存储,这个方法有能力去访问所在类的自由变量. Java中闭包实现 关键点: 如何用变量去存储方法? java中能够保存方法的变量指的就是普通的对象 如何让这个普通对象能够访问所在类的自由变量?

转:深入理解JavaScript闭包概念

闭包向来给包括JavaScript程序员在内的程序员以神秘,高深的感觉,事实上,闭包的概念在函数式编程语言中算不上是难以理解的知识.如果对作用域,函数为独立的对象这样的基本概念理解较好的话,理解闭包的概念并在实际的编程实践中应用则颇有水到渠成之感. 在DOM的事件处理方面,大多数程序员甚至自己已经在使用闭包了而不自知,在这种情况下,对于浏览器中内嵌的JavaScript引擎的bug可能造成内存泄漏这一问题姑且不论,就是程序员自己调试也常常会一头雾水. 用简单的语句来描述JavaScript中的闭

关于对象的行为、数组、继承和类的高级概念

1.对象的行为:                   (1)方法调用栈:所有的方法调用都维护在一个称为调用栈的结构中. 第一个被调用的方法就是main(),该方法是Jvm调用的,因此main()方法总是在调用栈的底部. (2)调用方法:一个方法被调用时该方法就放在调用栈的顶部,知道方法执行完成. 三种情况:①方法返回一个值:一个基本数据类型或一个引用数据类型被传回给方法的调用者: ②方法不返回一个值:返回值被声明为void:  ③方法抛出一个异常给方法的调用者. Java中的方法只能出现在类中,