开始
关于委托,肯定是要有问题的。
第一个问题,委托用来干什么?
看.net中的表述:在.net平台下,委托类型用来定义和相应应用程序中的回调。(回调?处理内存中两个实体双向通信的一种技术。)
第二个问题,委托和C++(其实起源于C语言)函数指针?
必然要说区别,虽然委托和函数指针都是指向以后要调用的方法。但委托相比于函数指针来说,主要有两点优势:一,委托是对象(我们知道,委托和类在一个层次)。二,委托是内置支持多路广播和异步方法调用的。
关于事件。
问题,事件用来干什么?
事件是为委托服务的,它使得我们处理委托类型的过程更简化和高效。
关于Lambda。
问题,这东西又是干什么的?
Lambda使用操作符=>来让我们调用委托,只不过更简单。
OK,看来,这都和委托密不可分,知道委托是个什么玩意,就能了解事件和Lambda是什么玩意了。
一、.net委托类型
之前的回调函数,C中的函数指针,也就是一个指向内存中一块地址完事。我们不了解更多的有关类型的信息,比如什么类型,什么返回值,妈蛋,都一抹黑。
委托出来了,觉得之前的那一套回调不安全,于是我变成类型安全的对象,指向程序中以后会被调用的方法(可以是多个。任性!)
1.委托能包含什么东西?有这些东西之后,有神马好处?
它所调用的函数方法名称;方法的参数(可选);方法的返回值(可选)。
有这些,在运行时我可以动态调用指向的方法。由此一来,本委托就自动有了同步或一部访问方法的能力,也就不用Thread那一套来调用辅助方法了,算是简化了编程工作。
2.委托干了什么,怎么干的?
定义:public delegate int BinaryOp(int x, int y);表示一个指向操作两个整数返回一个整数的方法。
当编译的时候,委托就会变成有三个公共方法的东东,三个方法:Invoke、BeginInvoke、EndInvoke方法,第一个用来同步调用的,后两个异步调用。而异步调用一般用来调用比较耗时的方法。(我们写界面的时候,经常会用到,防止界面卡逼,就异步调用,不影响界面的移动和其他的操作。)
在内存中她们的表示经常如下(其中下划线为红线的可以写成返回类型和参数类型(不论个数)):
由上面的那个伪代码就能看得出来,委托其实是和类在一个层次的东西,它还有基类,两个基类:System.MulticastDelegate和System.Delegate基类。
简单的委托实例(两个数的加法):
怎么干?委托在底层,实际上在MulticastDelegate派生类上调用了编译器生成的invoke方法。
说下委托的类型安全:如果试图将一个不匹配的方法传入委托,就会收到编译错误(例如上面的委托你传入一个参数的方法。)
3.委托怎么发对象通知?
定义一个Car类,他的公共成员如下:
定义委托,来判断,当加速的时候,会不会超过最大值而崩溃:
下面是加速方法,当速度不同的时候输出一些不同的信息:
在Program的Main方法里面每次加速 二十,并且记录每次加速之后的输出:
调用结果如下:
由上,可以总结出委托通知的步骤:
定义将通知发送给调用者的委托类型。
声明每个委托类型成员变量。
在泪殇创建辅助函数使调用者能指定由委托成员变量保存的方法。
修改方法,在适当的情形下调用委托中的调用列表。
多路广播是怎么回事?
意思就是说,一个委托对象可以维护一个可调用方法的列表,而不只是单独的一个方法。how?就涉及到+=和-=操作符了。
上面的委托调用方法再写一个:
Car中的方法修改:
调用代码:
结果:
同理,如果是想要移除方法,用-=操作符就可以了。相当于退订功能。
4.方法组转换语法
C#提供的方法组转换的语法,方便了委托操作,允许我们在调用委托作为参数的方法时候直接提供方法的名称,而不用创建委托对象。
如,上面的创建委托对象的过程都可以省了,成为如下代码:
c1.RegistWithCarEngine(OnCarEngineEvent2);
5.委托协变
拿上面的那个Car举例子来说,如果我们派生了一个NewCar的类,并创建一个委托类型,可以指向返回该类的方法。因为委托是安全类型,我们不能用返回Car累的委托,必须再定义一个新委托。
而委托协变,就是允许我们创建一个委托能指向返回类以及相关继承体系的方法。
我们可以将上面一个返回Car的委托结果,进行强制转换获得。
CarDelegate car = new carDelegate(new NewCar(xxx));用的时候,NewCar newCar = (NewCar)car;。
6.泛型委托
C#允许我们定义泛型委托类型,例如假设我们希望定义一个委托类型来调用任何返回void并且接受单个参数的方法。如果这个参数可能会不同,我们就可以通过类型参数来构建。
泛型委托提供了更灵活的方式来指定类型安全的形式进行调用的方法。
之前,我们一般通过使用System.Object参数来达到相似的目的:
public delegate void MyDelegate(object arg);
尽管如此,但是我们会因此失去类型安全并且可能还会有装箱损失。
二、C#事件
使用委托会遇到的问题。(破坏封装性)
我们没把委托定义为私有的情况下,调用者就可以直接访问委托对象,这样,调用者还可以重新把变量赋值给新的委托对象。Ooops!这下问题就出现了。
之前的方法相当于删了,并且调用者还可以直接调用委托的调用列表。
一句话说:公共成员打破了封装,不仅会导致代码难以维护和调试,还会导致应用程序的安全风险。
1.事件是干毛的?怎么干?
上面说了问题,事件就出来了,就可以知道,事件就是干这玩意的。(包装委托,使其不破坏封装性)。
怎么实现的呢?在编译器处理event关键字的时候,它会自动提供注册和注销方法以及任何必要的委托类型成员变量(貌似还简化了委托),并且这些委托成员变量总是私有的,因此不可能从触发事件的对象访问它们(ooh,安全了)。
事件的用法:
下面是修改之后的加速方法:
2.那么事件是如何封装委托的?(除了默认将委托置为private,还有下面的封装)
add_和remove_前缀。比如上面的AboutToBlow事件,在CIL代码中封装如下:
由此可以看来,事件只是节省了键入事件,下面说说如何在调用者这边监听传入的事件。
C#事件也简化了注册调用者事件处理程序的操作。现在无需指定自定义辅助方法,调用者仅需使用+=和-=操作符即可(操作符将在后台触发正确的add_xxx()方法或remove_xxx()方法)。
使用C#的事件注册语法修改Main方法如下:
当然,我们也可以用方法组转换语法。就不再陈述。
3.创建自定义的事件参数。
微软推荐的事件模式是什么样子的呢?
查看基础类库中某个类型发送的事件时,会发现底层委托的第一个参数是一个System.Object,第二个参数是一个派生自System.EventArgs的子类型。
System.EventArgs基类,表示一个不发送任何自定义信息的事件:
所以,如果我们如果要传递自定义数据,那么就需要创建一个派生自EventArgs的类。如下:
我们修改委托,增添一个CarEventArgs参数,就可以传递数据了。(事件不变。)
public delegate void CarEngineHandler(object sender, CarEventArgs args);//定义委托
调用方法:
if (Exploded != null)
{
Exploded(this,new CarEventArgs("Sorry, this car is dead..."));
}
如何接受者想与发送事件的对象交互,我们可以现实强制转换System.Object。这样就可以使用传递给事件通过对象中的任何公共成员。
三、C#匿名方法
匿名方法又是干毛的?
这可真是一波一波的,先是委托解决了函数指针的不安全性,但是后来嫌麻烦,又出现了事件。结果,事件也看起来啰里啰嗦,好吧,匿名方法登场:
上面的注册事件处理程序,被改成了:
c1.AboutToBlow += delegate (object sender,CarEventArgs e)
{
Console.WriteLine("Eek! Going to fast!");
};
看起来似乎是处理和注册声明到一块儿了。
说明:匿名方法中最后一个大括号必须以分好结束,否则,将产生一个编译错误。
下面是匿名方法的语法的伪代码:
匿名方法非常有趣,它使我们能访问定义他们的方法的本地变量。这些变量成为匿名方法的外部变量。有关匿名方法和定义方法的作用域之间的交互,有几个重要的知识点:
匿名方法不能访问定义方法中的ref或out参数。
匿名方法中的本地变量不能与外部方法中的本地变量重名。
匿名方法可以访问外部类作用域中的实例变量(或静态变量)。
四、Lambda表达式
Lambda表达式是干毛用的?好吧,彻底被干败了,觉得匿名方法还麻烦,都把裤子脱了还嫌麻烦,于是就让你比脱了裤子还简单,看我Lambda的吧:
编写:Lambda表达式是这样编写的,首先,定义一个参数列表,“=>”标记紧随其后,然后就是处理这些参数的语句。下面表达式可以说明:
ArgumentsToProcess=>StatementsToProcessThem。
那么用Lambda表达式,如何表达上面的注册处理程序?如下:
c1.AboutToBlow +=(sender,e)=>{ Console.WriteLine("Eek! Going to fast!");//在里面可以处理传入的参数 };
五、小结
这主要讨论了四个东西,其实就是一个,那就是委托,然后优化成事件。然后简化成匿名方法,最后简化成Lambda表达式的过程。
- C# Note2:委托(delegate) & Lambda表达式 & 事件(event)
前言 本文主要讲述委托和Lambda表达式的基础知识,以及如何通过Lambda表达式实现委托调用,并阐述.NET如何将委托用作实现事件的方式. 参考:C#高级编程 1.什么是委托(delegate)? ...
- C# 委托、lambda表达式和事件
什么是委托?委托就是持有一个或多个方法的对象,并且该对象可以执行,可以传递. using System; using System.Collections.Generic; using System. ...
- 委托、Lambda表达式、事件系列07,使用EventHandler委托
谈到事件注册,EventHandler是最常用的. EventHandler是一个委托,接收2个形参.sender是指事件的发起者,e代表事件参数. □ 使用EventHandler实现猜拳游戏 使用 ...
- 委托、Lambda表达式、事件系列06,使用Action实现观察者模式,体验委托和事件的区别
在"实现观察者模式(Observer Pattern)的2种方式"中,曾经通过接口的方式.委托与事件的方式实现过观察者模式.本篇体验使用Action实现此模式,并从中体验委托与事件 ...
- 委托、Lambda表达式、事件系列05,Action委托与闭包
来看使用Action委托的一个实例: static void Main(string[] args) { int i = 0; Action a = () => i++; a(); a(); C ...
- 委托、Lambda表达式、事件系列04,委托链是怎样形成的, 多播委托, 调用委托链方法,委托链异常处理
委托是多播委托,我们可以通过"+="把多个方法赋给委托变量,这样就形成了一个委托链.本篇的话题包括:委托链是怎样形成的,如何调用委托链方法,以及委托链异常处理. □ 调用返回类型为 ...
- 委托、Lambda表达式、事件系列03,从委托到Lamda表达式
在"委托.Lambda表达式.事件系列02,什么时候该用委托"一文中,使用委托让代码简洁了不少. namespace ConsoleApplication2 { internal ...
- 委托、Lambda表达式、事件系列02,什么时候该用委托
假设要找出整型集合中小于5的数. static void Main(string[] args) { IEnumerable<int> source = new List<int&g ...
- 委托、Lambda表达式、事件系列01,委托是什么,委托的基本用法,委托的Method和Target属性
委托是一个类. namespace ConsoleApplication1 { internal delegate void MyDelegate(int val); class Program { ...
- 委托学习过程及委托、Lambda表达式和匿名方法的关系总结及事件总结
第一章,当开始学习委托的时候,我们会问什么是委托?为什么要学习委托? 一,什么是委托? 委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法, ...
随机推荐
- MySQL高级知识- MySQL的架构介绍
[TOC] 1.MySQL 简介 概述 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB公司开发,目前属于Oracle公司. MySQL是一种关联数据库管理系统,将数据保存在不同的表中,而 ...
- 一看就懂的ReactJs入门教程-精华版
现在最热门的前端框架有AngularJS.React.Bootstrap等.自从接触了ReactJS,ReactJs的虚拟DOM(Virtual DOM)和组件化的开发深深的吸引了我,下面来跟我一起领 ...
- C# 发送邮件 附件名称为空
示例代码: // 1.创建邮件 MailMessage mailMsg = new MailMessage(); mailMsg.To.Add(new MailAddress("test@ ...
- 窥探Vue.js 2.0 - Virtual DOM到底是个什么鬼?
引言 你可能听说在Vue.js 2.0已经发布,并且在其中新添加如了一些新功能.其中一个功能就是"Virtual DOM". Virtual DOM是什么 在之前,React和Em ...
- Android公共title的应用
我们在开发Android应用中,写每一个页面的时候都会建一个title,不是写一个LinearLayout就是写一个RelativeLayout,久而久之就会觉得这样繁琐,尤其几个页面是只是标题不一样 ...
- Golang 编写的图片压缩程序,质量、尺寸压缩,批量、单张压缩
目录: 前序 效果图 简介 全部代码 前序: 接触 golang 不久,一直是边学边做,边总结,深深感到这门语言的魅力,等下要跟大家分享是最近项目 服务端 用到的图片压缩程序,我单独分离了出来,做成了 ...
- jsp页面无法识别el表达式的解决方案
今天在写一个springmvc的小demo时,碰到一个问题,在jsp页面中书写为${user.username}的表达式语言,在浏览器页面中仍然显示为${user.username},说明jsp根本不 ...
- BZOJ 1010: [HNOI2008]玩具装箱toy [DP 斜率优化]
1010: [HNOI2008]玩具装箱toy Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 9812 Solved: 3978[Submit][St ...
- 面向未来的友好设计:Future Friendly
一年前翻译了本文的一部分,最近终于翻译完成.虽然此设计思想的提出已经好几年了,但是还是觉得应该在国内推广一下,让大家知道“内容策略”,“移动优先”,“响应式设计”,“原子设计”等设计思想和技术的根源. ...
- 微信硬件H5面板开发(二) ---- 实现一个灯的控制
在第一节中讲解了openApi的调用,这一篇讲一下如何实现一个灯的控制.就用微信提供的lamp例子来做,将代码扒下来(实在是没办法,没有示例),整合到自己的项目中.lamp源码:http://file ...