事件,定义了事件成员的类型允许类型或类型的实例通知其它对象发生了特定的事情。

按照我自己的理解而言,事件可以被(方法)关注,也可以被(方法)取消关注,事件发生后关注了事件的一方会了解到,并对事件做出相应的应对(执行方法)。(我每次都是这么理解的,这样从字面意义上更好理解一点)

众所周知,事件实际上就是基于委托的。而委托是调用回调函数的一种类型安全的方式。

今天写一个关于分手的事件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 事件的更多相关文章

  1. JavaScript进阶系列06,事件委托

    在"JavaScript进阶系列05,事件的执行时机, 使用addEventListener为元素同时注册多个事件,事件参数"中已经有了一个跨浏览器的事件处理机制.现在需要使用这个 ...

  2. JavaScript进阶系列05,事件的执行时机, 使用addEventListener为元素同时注册多个事件,事件参数

    本篇体验JavaScript事件的基本面,包括: ■ 事件必须在页面元素加载之后起效■ 点击事件的一个简单例子■ 为元素注册多个点击事件■ 获取事件参数 ■ 跨浏览器事件处理 □ 事件必须在页面元素加 ...

  3. laravel进阶系列--通过事件和事件监听实现服务解耦

    简介 Laravel 事件提供了简单的观察着模式实现,允许你订阅和监听应用中的事件.事件类通常存放在 app/Events 目录. 监听器存放在 app/Listeners. 如果你在应用中没有看到这 ...

  4. .Net进阶系列(11)-异步多线程(委托BeginInvoke)(被替换)

    一. BeginInvoke最后两个参数的含义 倒数第二个参数:指该线程执行完毕后的回调函数:倒数第一个参数:可以向回调函数中传递参数. 下面以一段代码说明: /// <summary> ...

  5. JavaScript进阶系列07,鼠标事件

    鼠标事件有Keydown, Keyup, Keypress,但Keypress与Keydown和Keyup不同,如果按ctrl, shift, caps lock......等修饰键,不会触发Keyp ...

  6. JavaScript进阶系列04,函数参数个数不确定情况下的解决方案

    本篇主要体验函数参数个数不确定情况下的一个解决方案.先来看一段使用函数作为参数进行计算的实例. var calculate = function(x, y, fn) { return fn(x, y) ...

  7. JavaScript进阶系列03,通过硬编码、工厂模式、构造函数创建JavaScript对象

    本篇体验通过硬编码.工厂模式.构造函数来创建JavaScript对象. □ 通过硬编码创建JavaScript对象 当需要创建一个JavaScript对象时,我们可能这样写: var person = ...

  8. JavaScript进阶系列02,函数作为参数以及在数组中的应用

    有时候,把函数作为参数可以让代码更简洁. var calculator = { calculate: function(x, y, fn) { return fn(x, y); } }; var su ...

  9. JavaScript进阶系列01,函数的声明,函数参数,函数闭包

    本篇主要体验JavaScript函数的声明.函数参数以及函数闭包. □ 函数的声明 ※ 声明全局函数 通常这样声明函数: function doSth() { alert("可以在任何时候调 ...

  10. Ext JS学习第十六天 事件机制event(一) DotNet进阶系列(持续更新) 第一节:.Net版基于WebSocket的聊天室样例 第十五节:深入理解async和await的作用及各种适用场景和用法 第十五节:深入理解async和await的作用及各种适用场景和用法 前端自动化准备和详细配置(NVM、NPM/CNPM、NodeJs、NRM、WebPack、Gulp/Grunt、G

    code&monkey   Ext JS学习第十六天 事件机制event(一) 此文用来记录学习笔记: 休息了好几天,从今天开始继续保持更新,鞭策自己学习 今天我们来说一说什么是事件,对于事件 ...

随机推荐

  1. IIS兼容模式设置

    X-UA-Compatible IE=EmulateIE7 来自为知笔记(Wiz)

  2. 实例详解 DB2 排序监控和调优

    实例详解 DB2 排序监控和调优http://automationqa.com/forum.php?mod=viewthread&tid=2882&fromuid=2

  3. android 开发 - 对图片进行虚化(毛玻璃效果,模糊)

    概述 IPAD,IPHONE上首页背景的模糊效果是不是很好看,那么在 Android中如何实现呢.我通过一种方式实现了这样的效果. 开源库名称:anroid-image-blur 一个android ...

  4. Android Weak Handler:可以避免内存泄漏的Handler库

    这是一个针对技术开发者的一个应用,你可以在掘金上获取最新最优质的技术干货,不仅仅是Android知识.前端.后端以至于产品和设计都有涉猎,想成为全栈工程师的朋友不要错过! android使用java作 ...

  5. 顺序执行到来的消息 actor

    在某项目里,有个 actor 需要做一些持久化的操作,这些操作耗时比较久,理应使用异步的代码来写,但是需求又强调每次只能做一个持久化操作,后来的请求应该等待.一个显然的做法是阻塞式的写,这样就能比较简 ...

  6. 网络存储-Samba、NAS---未知的用户名或错误密码

    项目中的文件需要保存到网络存储设备中,之前用的是NAS.因没来得及采购就先用Samba顶上.代码发现通用…… 一.定义: Samba是在Linux和UNIX系统上实现SMB协议的一个免费软件,由服务器 ...

  7. 重构第2天:方法搬移(Move Method)

    现在就重构来说是非常普通的,虽然我们经常会漏掉或忽略一些需要重构的地方.方法搬移,正如所定义的那样,把方法搬移到更适合他的位置.让我们看看下面这一段重构前的代码: 理解:方法搬移,正如所定义的那样,把 ...

  8. CRUD Operations In ASP.NET MVC 5 Using ADO.NET

    Background After awesome response of an published by me in the year 2013: Insert, Update, Delete In ...

  9. hbase安装

    HBase的安装 本篇介绍两种HBase的安装方式:本地安装方式和伪分布式安装方式. 安装的前提条件是已经成功安装了hadoop,而且hadoop的版本要和hbase的版本相匹配. 我将要安装的hba ...

  10. Entity Framework访问MySQL数据库的存储过程并获取返回值

      一.创建MySql存储过程 1, CREATE PROCEDURE `InsertAlarmInfo`(in businessindex int, in providerindex int, in ...