.NET进阶篇-语言章-2-Delegate委托、Event事件

知识只有经过整理才能形成技能

整个章节分布简介请查看第一篇

内容目录

一、概述

二、解析委托知识点

1、委托本质

2、委托的使用

3、委托意义

逻辑解耦,减少重复代码

代码封装支持扩展

匿名方法和Lambda表达式

异步多线程

多播委托

三、事件

四、总结

一、概述

先说下委托,委托我们也经常用到。详尽了解委托是必要的,不然在异步多线程的编程中会一头雾水。委托本质就是一个类,和我们平常定义的类没多大区别。只是这个类的作用的是描述一些方法,没有数据成员。一个委托定义了一类拥有同样返回类型和参数的方法规范。委托的声明语法就是一个没有方法体的方法前面加上delegate关键字。既然本质是一个类,那它就可以在任何可以定义普通类的位置来定义委托。委托是一个能把方法作为参数传递的对象

事件就简单了,事件就是委托的一个实例

二、解析委托知识点

1、委托本质。

在VS中编码中,声明委托后,会发现委托的着色提示和类时一样。

但好像不是很有说服力。高级语法都做了很好的封装,方便编码人员。.NET的二次编译,第一次编译成IL中间语言,中间语言也是一种编程语言,只是它不像高级语言那么方便人类阅读。我们可以通过一些工具(像ILSpy)反编译来窥探下它的内部逻辑。

如图中红框所示,我们定义的普通类MyDelegate和委托类型NoReturnPara(继承自MulticastDelegate)是一致的,都是class。在委托类型NoResultNoPara中也有.ctor(在IL中构造函数),此外还有我们以后会经常用到的Invoke方法和BeginInvoke、EndInvoke方法,前者是同步调用,后者是异步调用

2、委托的使用

我们使用委托一般就是三步走,第一步定义委托,第二部声明委托实例,第三部调用。定义委托就像上面所示在一个没有方法体的方法前加上delegate关键字即可。它给定了一种约束,只能用规定的方法结构(返回值和参数)的实例化委托。

public delegate void NoReturnNoPara();//1 声明委托NoReturnNoPara method = new NoReturnNoPara(this.DoNothing);//2 委托的实例化method.Invoke();//3 委托实例的调用private void DoNothing(){        Console.WriteLine("This is DoNothing");}

委托的实例实际就是代表你绑定的方法,把方法包装成一个变量,Invoke时自动执行,这样用委托实例的调用就和直接方法调用效果一样。那这样有什么用呢?用处太大了。这样意味着你可以把一个方法当做参数传递给另一个方法,这么说好像也体会不到。类比下我们泛型的用途,泛型用于设计和类型无关的对象功能。那委托就是用于设计和方法无关的,可以将方法行为(逻辑)在从外部注入到对象内部。

3、委托意义

逻辑解耦,减少重复代码

假如有以下集合List,Student有身高、年龄属性。我们需要写一个方法查询出身高超过180cm的学生。(先不用List的Find、Where等方法,他们参数都是需要委托的,我们先考虑不用委托实现)。我们的方法可能像下面这样

然后如果需要查询出年龄大于20岁的呢,增加参数?方法重载?显然都要破坏原有类的封装。那我们想到“甩锅”,把变化的逻辑甩给调用者。上面两种查询也只是student.Height>180和student.Age>20判断逻辑的不同,可以把这部分当做参数传递进来。那么一组逻辑包装成变量,这就是委托。一个方法委托了我,你调用我就是调用那个方法。改造后的方法像下面这样

这样我们只需要在外部调用的地方修改或增加相应的逻辑方法就可以。这里就相当于自己实现了List的Where扩展方法,这也是它的内部原理。

但这样是不是也挺繁琐的,还要定义委托、定义个方法,然后实例化委托,好繁琐。既然委托规定了方法规范,那如果方法里不依赖的具体的类型,我们随意指定方法的参数类型该多好,还记得上节说的泛型吗。这里就使用泛型委托,我们也不自己去定义了,.NET为我们封装了通用的两类泛型委托Action<T>和Func<T>,前者代表无返回值,后者代表有返回值,每一类泛型委托都有十几个泛型参数可以指定,绝对够你用了。我们基本不用自己定义委托。这样还不够,我们还是要定义方法的呀。

代码封装,支持扩展

既然委托实例就是一个方法,结合泛型,那我们还可以做些更有趣的事情,下面实例代码的左右就是给一个方法增加了异常处理。(这只是无返回值的,你也可以加一个有返回值的)。这样就做到了,如果你的方法是指定给委托的,那么就可以捕获异常,你大可以不在具体方法内处理异常(实际,还是老老实实处理,这只是最后一道门)。我们甚至可以在调用任何方法前后加上日志,而不用修改原类的封装。有点类似AOP(面向切片编程)的味道了。

匿名方法和Lambda表达式

有时候简单的逻辑我们不必编写一个指定的方法。在实例化委托时可以直接指定一个匿名方法。像下面这样。优点当然是减少代码的复杂度了,还可以访问匿名方法外部的变量,但匿名方法内部不能使用break,continue等跳转语句。用来做简单的逻辑。

在C#3.0开始,我们有了Lambda表达式代替匿名方法,它比匿名方法更加简单。Lambda运算符“=>”(发音goesto)的左边列出了需要的参数,右边是利用该参数方法的实现代码。如果表达式只有一个语句,还可以像图二那样简写。

异步多线程

异步多线程的实现都是基于委托的。开篇我们看到委托有BeginInvoke和EndInvoke,这里先简单介绍下,我们会在后面异步多线程编程中详细解读。简单的代码如下图所示,结果会发现BeginInvoke实际是启用一个线程来调用方法的

BeginInvoke方法触发你的异步方法,它和你想要执行的异步方法有相同的参数。另外还有两个可选参数,第一个是AsyncCallback委托是异步完成的回调方法。第二个是用户自定义对象,该对象将传递到回调方法中。BeginInvoke立即返回并且不等待完成异步的调用(继续执行该下面的代码,不需要等待)。BeginInvoke返回IAsyncResult接口,可用于检测异步调用的过程。通过EndInvoke方法检测异步调用的结果。如果异步调用尚未完成,EndInvoke将阻塞调用线程,直到它完成。

多播委托

前面使用的每个委托都只包含一个方法的调用。调用委托的次数和调用方法的次数相同。如果要调用多个方法,就需要多次显示调用这个委托(就像多次调用一个方法一毛一样)。委托也可以包含多个方法。这种委托称为多播委托,但要注意如果方法有返回值,则只能得到委托调用最后一个方法的结果

使用运算符“+=”、“-=”来增加或去除委托的方法。+= 为委托实例按顺序增加方法,形成方法链,Invoke时,按顺序依次执行。-= 为委托实例移除方法,从方法链的尾部开始匹配,遇到第一个完全吻合的,移除且只移除一个,没有也不异常。但有一点要注意,多播委托时是不能使用异步的。

三、事件

事件也几乎无处不在,它提供一种发布/订阅机制。我们做桌面开发,Button类提供的Click事件。触发Click事件时调用的方法需要定义,其参数类型由委托类型定义。事件就是带event关键字的委托的实例,event可以限制变量被外部调用/直接赋值。事件的标准用法如下,(当然你也可以自己定义更有意义的委托类型来代替Action)。

事件就是委托的实例,所以事件能干的事情,普通的委托实例也都能干。只是为了某些场景下,事件规范了使用方法。如,事件只能用过+=来注册方法,只能在方法外部声明在内部调用,普通委托实例多用于回调,而事件多用于外部接口。后面设计模式中的观察者模式就是其典型应用。

四、总结

其实感觉理的不是很细,主要还是知识点的扫盲巩固,再往细了去总结可能就会车轱辘话反反复复的了。
委托事件,我们平常会很常用的。特别是如果进阶一下,异步多线程编程时会用的更多,没有委托就没有异步多线程。委托就是描述一类方法的类型,委托的实例就是代表一个方法。我们把一个方法当做一个委托的实例就可以进行传递,对于解耦有奇效。事件是委托的实例,最终是用来完成某一业务逻辑的一部分,只是这部分会变化,那么就把变化的形成封装出去,交给上层来指定,通过事件可以提供一个供外部扩展动作的接口,这样就会更加的灵活。规定动作我内部写死,扩展的交给外部。

如果手机在手边,也可以关注下vx:xishaobb,互动或获取更多消息。当然这里也一直更新de。

原文地址:https://www.cnblogs.com/xibei/p/11666161.html

时间: 2024-08-03 06:05:39

.NET进阶篇-语言章-2-Delegate委托、Event事件的相关文章

.NET进阶篇-语言章-1-Generic泛型深入

内容目录 一.概述二.泛型的好处三.泛型使用1.泛型方法2.泛型类.泛型接口四.泛型的功能1.泛型中的默认值2.约束3.协变逆变5.泛型委托4.泛型缓存五.总结 一.概述 泛型我们一定都用过,最常见的List<T>集合..NET2.0开始支持泛型,创建的目的就是为了不同类型创建相同的方法或类,也包括接口,委托的泛型.比如常见的ORM映射,一个方法通过传入不同的类,返回不同的类实例,再调用时才确定参数类型. 我们知道想要一个类相同名称的方法,如果仅参数类型不同,那么要重载.重载会有很多冗余的代码

5. 第二章:C#委托和事件

第二章:C#委托和事件 第二节:事件 事件其实没什么不好理解的,声明一个事件不过类似于声明一个委托类型的变量而已. 1 public class MyTestClass 2 { 3 //定义委托 4 public delegate void MyDelegateHandler(string param); 5 6 //构造函数 7 public MyTestClass() 8 { 9 this.MyDelegateHappened += this.MyInsideDelegateMethod;

6. 第二章:C#委托和事件

第二章:C#委托和事件 第三节:Action<T>和Func<T,TResult> 这些都是在.net framework 3.5新增的委托类型,在.net framework 2.0里面,我们用的委托是Delegate 过多的理论叙述不是我们的目的,这里我们用示例的方式展示给大家看一看这些新增的委托的用法:(涉及到函数的重载和泛型的知识点,C#基础不在此处赘述.) 1. Action / Action<T> / Action<T,S> / Action&l

慕课网javascript 进阶篇 第九章 编程练习

把平常撸的码来博客上再撸一遍既可以加深理解,又可以理清思维.还是很纯很纯的小白,各位看官老爷们,不要嫌弃.最近都是晚睡,昨晚也不例外,两点多睡的.故,八点起来的人不是很舒服,脑袋有点晕呼呼,鉴于昨晚看到了这章的编程练习,想着自己DOM编程艺术也差不多看完了,高级3也看了点,所打算开始多敲代码了. 谁知,看着编辑器一脸懵逼,不知道如何啃下这块骨头,米思绪,写着也没什么感觉,索性就不写了,这不下午才来撸它. 要求如下: <!DOCTYPE html> <html> <head&g

对张子阳先生对委托和事件的两篇文章的读后思考(说得很透,内附故事一篇)

第一篇 C#中的委托和事件 第二篇 C#中的委托和事件(续) 首先,张子阳先生的这是两篇关于委托和事件间关系的文章,是目前为止我读过的介绍委托和事件以及异步调用最简明清晰文章,作者通过非常有节奏的"标题"->"问题"->"思路"->"实现"->"讲解"的结构,分步骤一步一步地将委托和事件的实现.应用与原理阐述得非常清楚,并且在行文期间将自己有趣的思考过程通过生动的语言表达了出来,使人

对张子扬显示的两篇委托和事件说得很透文章读后的思考

第一篇 C#中的委托和事件 http://www.tracefact.net/CSharp-Programming/Delegates-and-Events-in-CSharp.aspx 第二篇 C#中的委托和事件(续) http://www.tracefact.net/CSharp-Programming/Delegates-and-Events-Advanced.aspx 这是两篇目前为止,我读过的介绍委托和事件以及异步调用最简明清晰文章,作者通过非常有节奏的"标题"->&q

浅谈委托和事件(一)

浅谈委托和事件(一) 关于委托和事件,可能是.NET或者说是面向对象编程语言中的一个比较重要又比较难以理解的概念.关于这一话题,园子里的人也写了很多文章,最经典的可能就是张子阳的C#中的委托和事件这两篇文章了,之前也看过MSDN 上的WebCast深入 "委托和事件".可能和很多人一样,刚开始读的时候,觉得很清楚,但是过了一段时间好像又忘记了委托和事件的区别,知道很久以前,在一次面试中我被问到委托和事件有什么区别,一下子就说不清了. 所以这里稍微理一下,也算是自己的一个总结.当然,还是

【.NET基础】--委托、事件、线程(3)

之前的两篇文章我们了解了委托和事件,本文我们看一下线程. 1,一个窗体程序,默认拥有一个线程(相当于一个商店里面,只有一个店员),这个默认的线程叫做 UI线程/主线程. 2,进程和线程的关系: A,进程,包含程序运行所需要的资源 ,在大多数情况下是指 程序.(商店:囤积要使用的资源的地方) B,线程,是在进程中能够被CPU调用的程序单元,是提供给CPU运行程序的代码片段.(商店员工:是运行程序的行动者) C,一个进程至少一个线程,每一个线程有自己专属的寄存器(栈指针.程序计数器等)但代码区是共享

java web进阶篇(三) 表达式语言

表达式语言(Expression Language ,EL)是jsp2.0中新增的功能.可以避免出现许多的Scriptlet代码 格式: ${ 属性名称 },  使用表达式语言可以方便的访问对象中的属性,提交的参数或者进行各种数学运算,而且使用表达式语言最大的特点是如果输出的内容是null,则会自动使用空字符串("")表示. <%request.setAttribute("name", "info");%> <h1>${n