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

内容目录

一、概述二、解析委托知识点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。

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

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

    内容目录 一.概述二.泛型的好处三.泛型使用1.泛型方法2.泛型类.泛型接口四.泛型的功能1.泛型中的默认值2.约束3.协变逆变5.泛型委托4.泛型缓存五.总结 一.概述 泛型我们一定都用过,最常见的 ...

  2. 第一章、C#委托和事件(Delegate、Event、EventHandler、EventArgs)

    第一章.C#委托和事件(Delegate.Event.EventHandler.EventArgs) 分类: 学习笔记-C#网络编程2012-12-08 14:10 7417人阅读 评论(3) 收藏  ...

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

    把平常撸的码来博客上再撸一遍既可以加深理解,又可以理清思维.还是很纯很纯的小白,各位看官老爷们,不要嫌弃.最近都是晚睡,昨晚也不例外,两点多睡的.故,八点起来的人不是很舒服,脑袋有点晕呼呼,鉴于昨晚看 ...

  4. IOS开发之进阶篇第一章 - 姿势识别器UIPanGestureRecognizer

    今天讲一下姿势识别器,UIGestureRecognizer这个是抽象类 1.拍击UITapGestureRecognizer (任意次数的拍击) 2.向里或向外捏UIPinchGestureReco ...

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

    之前的两篇文章我们了解了委托和事件,本文我们看一下线程. 1,一个窗体程序,默认拥有一个线程(相当于一个商店里面,只有一个店员),这个默认的线程叫做 UI线程/主线程. 2,进程和线程的关系: A,进 ...

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

      之前的两篇文章我们了解了委托和事件,本文我们看一下线程. 1,一个窗体程序,默认拥有一个线程(相当于一个商店里面,只有一个店员),这个默认的线程叫做 UI线程/主线程. 2,进程和线程的关系: A ...

  7. 前端开发工程师 - 02.JavaScript程序设计 - 第2章.进阶篇

    第2章--进阶篇 类型进阶 类型: Undefined Null Boolean String Number Object 原始类型(值类型):undefined, null, true, " ...

  8. MYSQL(进阶篇)——一篇文章带你深入掌握MYSQL

    MYSQL(进阶篇)--一篇文章带你深入掌握MYSQL 我们在上篇文章中已经学习了MYSQL的基本语法和概念 在这篇文章中我们将讲解底层结构和一些新的语法帮助你更好的运用MYSQL 温馨提醒:该文章大 ...

  9. 进阶篇,第二章:MC与Forge的Event系统

    <基于1.8 Forge的Minecraft mod制作经验分享> 这一章其实才应该是第一章,矿物生成里面用到了Event的一些内容.如果你对之前矿物生成那一章的将算法插入ORE_GEN_ ...

随机推荐

  1. CF1009B Minimum Ternary String 思维

    Minimum Ternary String time limit per test 1 second memory limit per test 256 megabytes input standa ...

  2. pyppeteer的使用

    pyppeteer的使用 安装 属于第三方模块进行安装. pip install pyppeteer 在Linux中,如果权限不够则加上. sudo pip install pyppeteer 使用 ...

  3. solr 的基本用法

    上图为 solr 的搜索页面,常用字段的基本用法如下: 1. q: 查询字符串,过滤条件,不能为空,必须输入,如果查询全部就写 * : * name:“马”  AND age:[0 TO 18]   ...

  4. Mysql的事务及行级锁

    转自:http://www.cnblogs.com/edwinchen/p/4171866.html 以签到为例,每个用户每天只能签到一次,那么怎么去判断某个用户当天是否签到呢?因为当初表设计的时候, ...

  5. 使用FlameGraph火焰图分析JAVA应用性能

    开源项目推荐 Pepper Metrics是我与同事开发的一个开源工具(https://github.com/zrbcool/pepper-metrics),其通过收集jedis/mybatis/ht ...

  6. postman--请求以及变量设置的实例练习

    我们可以在2个地方添加需要执行的js脚本,一个是Pre-request Script,还有一个tests,我们先看请求之前的 1 在请求被发送到服务器之前:就是在“Pre-request Script ...

  7. android 解决 多品牌手机拍照问题,尤其是小米手机

    先上个图吧 .点击头像弹出下面对话框,然后直接上代码. 头像是自定义控件实现的圆形头像,当然就目前而言 想要实现 圆形头像的资料太多了,随便找个就行 <com.kuibu.jucai.widge ...

  8. 使用JAVA API获取hadoop集群的FileSystem

    所需要配置的参数:  Configuration conf = new Configuration();   conf.set("fs.defaultFS", "hdfs ...

  9. ASP.NET Core 2.2 : 二十三. 深入聊一聊配置的内部处理机制

    上一章介绍了配置的多种数据源被注册.加载和获取的过程,本节看一下这个过程系统是如何实现的.(ASP.NET Core 系列目录) 一.数据源的注册 在上一节介绍的数据源设置中,appsettings. ...

  10. go语言-最大32位数反转

    package main import ( "fmt" "strconv" ) func fanzhuang32(number int) string { fu ...