【C#进阶系列】11 事件
事件,定义了事件成员的类型允许类型或类型的实例通知其它对象发生了特定的事情。
按照我自己的理解而言,事件可以被(方法)关注,也可以被(方法)取消关注,事件发生后关注了事件的一方会了解到,并对事件做出相应的应对(执行方法)。(我每次都是这么理解的,这样从字面意义上更好理解一点)
众所周知,事件实际上就是基于委托的。而委托是调用回调函数的一种类型安全的方式。
今天写一个关于分手的事件Demo,算是生动形象吧。
定义事件参数类(可忽略这步)
一个事件发生后若要传递附加的参数信息,就需要定义事件参数类,需要继承EventArgs,否则就直接使用EventArgs.Empty即可。(EventArgs.Empty实际上就是new EventArgs())
public class 分手EventArgs : EventArgs
{
public 分手EventArgs(string name, string title)
{
this._分手的人 = name;
this._分手原因 = title;
}
public string 分手的人
{
get
{
return _分手的人;
}
}
public string 分手原因
{
get
{
return _分手原因;
}
}
private readonly string _分手的人;
private readonly string _分手原因;
}
定义事件成员
public class Troy {
//委托类型EventHandler<T>的声明public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
//第一个对象顾名思义是事件发出者,这里肯定就是Troy发生了事件
//第二个参数传递的就是我们之前定义的事件附加信息
public event EventHandler<分手EventArgs> 宣称要分手;
}
现在有了一个Troy的宣称分手事件。
既然事件有了,那么接下来就是去让本人去引发这个事件
引发事件
于是就变成了这样
public class Troy {
//委托类型EventHandler<T>的声明public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
//第一个对象顾名思义是事件发出者,这里肯定就是Troy发生了事件
//第二个参数传递的就是我们之前定义的事件附加信息
public event EventHandler<分手EventArgs> 宣称要分手;
//定义负责引发事件的方法来通知已关注事件的人,一般是要定义为protected和virtual
protected virtual void On宣称要分手(分手EventArgs e) {
//如果有人听我说这个事,那么就说,没人听我就肯定不说了,你在程序里玩自言自语也不是不行,不过显得很傻而已(完美贴切好形象)
if(宣称要分手!=null)this.宣称要分手(this,e);
}
}
本书中其实还介绍了一个出于线程安全考虑的引发事件的写法,
因为如果在我宣称要分手前,本来听我讲这个事的人A有另一个人B叫他,A他突然跑掉了(在另一个线程中 宣称要分手 的委托链就被移除了委托),那么此时 宣称要分手 这个事件就没人听了(宣称要分手为null),然后我酝酿了半天的话吐不出来就报了个Null异常。
所以有了下面这种写法
protected virtual void On宣称要分手(分手EventArgs e) {
//下面代码的意思就是:我要说分手的时候将关注了 宣称要分手 这个事件的人都拉到讨论组中
//然后就算被另一个线程的人将在我旁边的人都叫走了,实际上因为我把他们丢拉到讨论组中了
//此时讨论组中都有成员,所以关注的人还是获悉了这个悲伤的故事
var 讨论组 = System.Threading.Volatile.Read(ref 宣称要分手);//Volatile.Read仅仅起到赋值宣称要分手的引用给讨论组,之所以不用等于,是因为可能会被编译器优化时去掉临时变量 讨论组。
if (讨论组 != null)this.宣称要分手(this,e);
}
然而JIT编译器表示,实际上这种用等于也可以,只是为了防范于未然。
然而此处我只想安静地分手,所以让我们沿用更上面的说法
真实的引发事件
我每天都可以说很多分手事件,有的是你,有的还是你。
为了更准确的把这个事给说清楚了,我也许还要传递个信息,就是这次可能要分手的是我。
public class Troy {
//委托类型EventHandler<T>的声明public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
//第一个对象顾名思义是事件发出者,这里肯定就是Troy发生了事件
//第二个参数传递的就是我们之前定义的事件附加信息
public event EventHandler<分手EventArgs> 宣称要分手;
//定义负责引发事件的方法来通知已关注事件的人,一般是要定义为protected和virtual,前者是要,后者是为了派生类的必要
protected virtual void On宣称要分手(分手EventArgs e) {
if(宣称要分手!=null)this.宣称要分手(this,e);
}
public void 宣称自己要分手() {
//这一步我们把事说清楚
var e = new 分手EventArgs("Troy", "心累");
On宣称要分手(e);//如果子类没有重写这个事件引发函数,那么就告诉所有关注的人这个事
}
}
事件类型被编译后会出现什么?
C#编译器将事件编译后会转换成3个东西:一个私有的委托字段 宣称要分手,一个公共的关注事件的方法 add_宣称要分手,一个公共的取消关注的方法 remove_宣称要分手。
除了这三个东西,编译器还会在托管程序集的元数据中生成一个事件定义记录项,它的作用只是为了建立“事件”的抽象概念和它的访问器方法之间的联系。(高深吧?然而你并不需要懂)
关于关注事件的那些人
事件以及事件的引发都弄好了,接下来就是定义关注事件的那些人了。以下人物由真实人物改编:
//下面是将婚同事李
public class Lee {
public Lee(Troy troy) {
//初始化就关注事件
troy.宣称要分手 += this.AfterListen;
}
private void AfterListen(Object sender, 分手EventArgs e) {
Console.WriteLine("让troy去玩游戏");
}
}
//然后是单身同事肖
public class Xiao {
public Xiao(Troy troy)
{
troy.宣称要分手 += this.AfterListen;
}
private void AfterListen(Object sender, 分手EventArgs e)
{
Console.WriteLine("表示Troy打击单身狗"); }
//由于Xiao并不是每天都和Lee一样,与Troy同行,所以Xiao也许开始听得到,后来跑远了,就取消关注事件了
private void UnListen(Troy troy) {
troy.宣称要分手 -= this.AfterListen;
}
}
一个对象只要某个方法关注了事件,那么它就不能被回收了。所以如果你想让垃圾处理器回收某对象,就让他不要再关注事件了。
好吧,就这些了。
PS:
这确实是一个真实的故事,就在昨天。
我也不知道我为什么还有心情写博客,反正除了这个我也已经不知道该干嘛好了。
大概是因为就算失恋了也不可能说不去吃饭睡觉之类的感觉吧。
更加令人惊奇的是,效率不知道为什么出奇得高,以至于十一点前就完成了学习和博客。
每个人都需要去成长,成长的故事大多都是不舒服的,一如熬夜学习写博客,一如分手这件事。
怀着感恩的心去面对已经失去的人,反而比什么样的疗伤都来得有效d(╯﹏╰)b
【C#进阶系列】11 事件的更多相关文章
- JavaScript进阶系列06,事件委托
在"JavaScript进阶系列05,事件的执行时机, 使用addEventListener为元素同时注册多个事件,事件参数"中已经有了一个跨浏览器的事件处理机制.现在需要使用这个 ...
- JavaScript进阶系列05,事件的执行时机, 使用addEventListener为元素同时注册多个事件,事件参数
本篇体验JavaScript事件的基本面,包括: ■ 事件必须在页面元素加载之后起效■ 点击事件的一个简单例子■ 为元素注册多个点击事件■ 获取事件参数 ■ 跨浏览器事件处理 □ 事件必须在页面元素加 ...
- laravel进阶系列--通过事件和事件监听实现服务解耦
简介 Laravel 事件提供了简单的观察着模式实现,允许你订阅和监听应用中的事件.事件类通常存放在 app/Events 目录. 监听器存放在 app/Listeners. 如果你在应用中没有看到这 ...
- .Net进阶系列(11)-异步多线程(委托BeginInvoke)(被替换)
一. BeginInvoke最后两个参数的含义 倒数第二个参数:指该线程执行完毕后的回调函数:倒数第一个参数:可以向回调函数中传递参数. 下面以一段代码说明: /// <summary> ...
- JavaScript进阶系列07,鼠标事件
鼠标事件有Keydown, Keyup, Keypress,但Keypress与Keydown和Keyup不同,如果按ctrl, shift, caps lock......等修饰键,不会触发Keyp ...
- JavaScript进阶系列04,函数参数个数不确定情况下的解决方案
本篇主要体验函数参数个数不确定情况下的一个解决方案.先来看一段使用函数作为参数进行计算的实例. var calculate = function(x, y, fn) { return fn(x, y) ...
- JavaScript进阶系列03,通过硬编码、工厂模式、构造函数创建JavaScript对象
本篇体验通过硬编码.工厂模式.构造函数来创建JavaScript对象. □ 通过硬编码创建JavaScript对象 当需要创建一个JavaScript对象时,我们可能这样写: var person = ...
- JavaScript进阶系列02,函数作为参数以及在数组中的应用
有时候,把函数作为参数可以让代码更简洁. var calculator = { calculate: function(x, y, fn) { return fn(x, y); } }; var su ...
- JavaScript进阶系列01,函数的声明,函数参数,函数闭包
本篇主要体验JavaScript函数的声明.函数参数以及函数闭包. □ 函数的声明 ※ 声明全局函数 通常这样声明函数: function doSth() { alert("可以在任何时候调 ...
- Ext JS学习第十六天 事件机制event(一) DotNet进阶系列(持续更新) 第一节:.Net版基于WebSocket的聊天室样例 第十五节:深入理解async和await的作用及各种适用场景和用法 第十五节:深入理解async和await的作用及各种适用场景和用法 前端自动化准备和详细配置(NVM、NPM/CNPM、NodeJs、NRM、WebPack、Gulp/Grunt、G
code&monkey Ext JS学习第十六天 事件机制event(一) 此文用来记录学习笔记: 休息了好几天,从今天开始继续保持更新,鞭策自己学习 今天我们来说一说什么是事件,对于事件 ...
随机推荐
- BW标准数据源初始化设置
在安装了一干补丁和做好了BW与R3的链接之后(此处有BISIS操心,具体事宜不详),我们就可以登录到R3系统看个究竟了. 磨刀不误砍柴工,先检查一下两边系统的补丁: R3端如下, ,貌似我们是19,通 ...
- Customer IEnuramble Extension
public static class IEnurambleExtension { public static IEnumerable<TSource> DistinctBy<TSo ...
- openssl命令行工具简介 - RSA操作
原文链接: http://www.cnblogs.com/aLittleBitCool/archive/2011/09/22/2185418.html 首先介绍下命令台下openssl工具的简单使用: ...
- MailMessage to EML
EML格式是微软公司在Outlook中所使用的一种遵循RFC822及其后续扩展的文件格式,并成为各类电子邮件软件的通用格式. 做个笔记,C# 邮件处理保存为eml格式: 一.网上好多这样的写法,可以在 ...
- win8 IIS
IIS打开页面报500错误 aspnet_regiis.exe -i 报 “此操作系统版本不支持此选项” 决解方法: 控制面板 - 程序和功能 - 启动或关闭windows功能 - Internet ...
- mongodb存储过程
MongoDB支持存储过程的使用,它的存储过程是用javascript实现的,被存在于system.js表中,可以接收和输出参数,返回执行存储过程的状态值,也可以嵌套调用. 所以我理解的Mon ...
- js 事件捕获与事件冒泡例子
http://codepen.io/huashiyiqike/pen/qZVdag addEventListener 默认是冒泡阶段执行,也就是父亲与子都监听时,点击子,子先处理,父亲再处理,这时加s ...
- JAVA笔记 之 Thread线程
线程是一个程序的多个执行路径,执行调度的单位,依托于进程存在. 线程不仅可以共享进程的内存,而且还拥有一个属于自己的内存空间,这段内存空间也叫做线程栈,是在建立线程时由系统分配的,主要用来保存线程内部 ...
- Linux探秘之用户态与内核态
一. Unix/Linux的体系架构 如上图所示,从宏观上来看,Linux操作系统的体系架构分为用户态和内核态(或者用户空间和内核).内核从本质上看是一种软件——控制计算机的硬件资源,并提供上层应用程 ...
- 【转载】C#之int与Java之Integer的区别
本文涉及到一些JVM原理和Java的字节码指令,推荐感兴趣的读者阅读一本有关JVM的经典书籍<深入Java虚拟机(第2版)>,将它与我在<.NET 4.0面向对象编程漫谈>中介 ...