C#事件具体实现步骤

作者:小菜 更新时间:2025-03-16 点击数:
简介:定义一个事件成员,表示该类型提供了如下功能:1.能够在事件中注册方法 2.能够在事件中注销方法 3.当事件发生时,注册的方法会被通知(事件内部维护了一个注册方法

【菜科解读】

定义一个事件成员,表示该类型提供了如下功能:

1.能够在事件中注册方法 2.能够在事件中注销方法 3.当事件发生时,注册的方法会被通知

(事件内部维护了一个注册方法列表)

CLR的事件模型是基于委托的,它可以通过类型安全的方式调用回调方法。

而回调方法是订阅事件的对象接收通知的方式。

通过一个例子来说明:

①Fax对象的方法注册到MailManager事件 ②Pager对象的方法注册到MailManager事件 ③新的邮件到达MailManager ④MailManager对象向注册的方法发出通知,接收通知的方法可以随意处理。

具体实现步骤如下:

1.定义一个类型,能够hold住任何发送到事件通知接收者的信息

当一个事件被触发,触发事件的对象可能希望发送一些额外的信息给事件通知的接收对象。

这些额外的信息需要封装在它自己的类中,根据约定该类需要从System.EventArgs类派生,并且命名以EventArgs结尾。

这里定义一个NewMailEventArgs类:

public class NewMailEventArgs : EventArgs private readonly String m_from, m_to, m_subject; public NewMailEventArgs(String from, String to, String subject) m_from = from; m_to = to; m_subject = subject; public String From { get { return m_from; } } public String To { get { return m_to; } } public String Subject { get { return m_subject; } } }

关于EventArgs

[ComVisible(true)][Serializable]public class EventArgs public readonly static EventArgs Empty; static EventArgs() EventArgs.Empty = new EventArgs(); public EventArgs()}

这个类没有实际的用途,只是作为一个基类让其他对象继承。

很多对象不需要传递额外的信息,例如按钮事件,只是调用一个回调方法就够了。

当我们定义的事件不需要传递额外的信息时,这时调用EventArgs.Empty就行了,不需要重新构建一个EventArgs对象。

2.定义事件成员

public class MailManager { ... //NewMail事件名, //EventHanlder,所有的事件通知接收对象必须提供给该委托类型匹配的回调方法 public event EventHandler NewMail; }

System.EventHandler委托的定义为:public delegate void EventHandler(Object sender, TEventArgs e) where TEventArgs: EventArgs;

为什么这里第一个参数sender的类型是Object?毕竟MailManager类型是唯一触发这个事件的,所以可以设计成这样:void MethodName(MailManager sender,NewMailEventArgs e)这种情况会有一个弊端,当sender是SmtpMailManager时,回调方法也需要改变,使用Object能够很好的兼容。

定义回调方法的参数名约定为e,这样做主要是为了保持一致性。

方便开发人员。

事件机制要求所有的事件处理方法必须返回void,这是必要的,因为一个事件可能触发很多的回调方法,没有办法获取所有的返回值,索性就不允许返回值,全部为void。

有些FCL里面的事件处理程序没有遵循,而是返回了一个Assembly类型。

3.定义一个方法来响应事件的发生

按照惯例,这个类应该定义一个protected,virtual的方法供内部的代码调用。

这个方法接收一个NewMailEventArgs对象,这个对象包含要传递给消息接收方的一些信息。

如下:

protected virtual void OnNewMail(NewMailEventArgs e) { //复制一个委托的引用到临时字段temp,这样确保线程安全 EventHandler temp = Interlocked.CompareExchange(ref NewMail, null, null); //任何注册到事件里面的方法,通知它们 if (temp != null) { temp(this, e); } }

Tips:使用线程安全的方式触发事件(①——>④为不断改进的过程)

①当.NET第一次推出的时候,给开发者推荐的事件触发方式如下:

//v1.0protected virtual void OnNewMail(NewMailEventArgs e) { if (NewMail != null) { NewMail(this, e); } }

弊端:这里检查了NewMail不为null才触发,但是当检查完之后,在调用NewMail之前,有其他的线程从委托链中移除了一个委托,使得NewMail为null,此时会抛出异常。

②先将NewMail用一个临时变量存起来,这时就不会因为调用时被其他线程修改而抛出异常。

之所以能够这样做,是因为委托类型跟字符串类型一样是不可变的。

//v2.0protected void OnNewMail(NewMailEventArgs e) { EventHandler temp = NewMail; if (temp != null) { temp(this, e); } }

弊端:可能被编译器优化掉本地temp变量,如果发生这种情况,就回到了第一种了。

③修复上面的bug,如下:

//v3.0protected void OnNewMail(NewMailEventArgs e) { EventHandler temp = Thread.VolatileRead(ref NewMail); if (temp != null) { temp(this, e); } }

这里使用VolatileRead会强制读取temp的值,但是这里不能这样写,编译不通过。

但是有一个Interlocked.CompareExchange可以使用:

//v4.0 protected virtual void OnNewMail(NewMailEventArgs e) { //复制一个委托的引用到临时字段temp,这样确保线程安全 EventHandler temp = Interlocked.CompareExchange(ref NewMail, null, null); //任何注册到事件里面的方法,通知它们 if (temp != null) { temp(this, e); } }

如果NewMail为null,CompareExchange将NewMail的值改变为null,如果不为null则返回原值。

换句话说,CompareExchange不会改变NewMail的值,只是以线程安全的方式返回NewMail的值,这里是一个原子操作。

第④个版本是最佳的,技术上最正确的版本。

实际开发中还是可以使用第②个版本,因为JIT编译器能够识别这种模式而不去优化本地的temp变量。

特别地,所有微软的JIT编译器都遵循不会对堆引入新的读取,因此缓存一个引用在本地变量可以确保堆引用只被访问一次(这是没有写入文档的,理论上,还是可能发生变化,所以最好选用第④版本。

为了方便可以定义一个扩展方法来封装:

public static class EventArgExtensions { public static void Raise(this TEventArgs e, Object sender, ref EventHandler eventDelegate) where TEventArgs : EventArgs { EventHandler temp = Interlocked.CompareExchange(ref eventDelegate, null, null); if (temp != null) { temp(sender, e); } } }

然后可以重写OnNewMail:

protected virtual void OnNewMail(NewMailEventArgs e) e.Raise(this, ref NewMail); }

4.定义一个方法用来传递一些输入到事件

public void SimulateNewMail(String from, String to, String subject) NewMailEventArgs e = new NewMailEventArgs(from, to, subject); OnNewMail(e); }123在本页阅读全文 本文导航 第1页: 首页 第2页: 编译器是怎么实现事件的? 第3页: 定义类型监听事件 事件,具体,实现,步骤,定义,一个,事件,成员,

泰国下降头的真实事件,男子泰国旅游被餐厅老板下食降

导语:  泰国是人们捡些年来出国旅游的首选圣地,因为在泰国旅游的话物价不高,酒店住宿也是十分亲民,重点是还有着许多的旅游胜地。

但其实泰国的各种邪教也有很多,就说下降头吧!虽然说如今科学发达,但是关于泰国下降头的真实事件发生了不少,并非都让人十分的毛骨悚然。

一、泰国下降头存在的真实事件  去之前朋友特地交代我要小心那里部分人养小鬼,下降头,当心中招。

当时的我也就当个笑话听了就过去了,谁知道中降头这种事情居然真的发生在了我的身上。

当天晚上,导游又带我们去了一个很差的餐厅,不知怎么的,我总觉得胸口很闷,好像有事情要发生的感觉。

一进餐厅,看到了坐在吧台旁边的四十岁左右的中年男子,牙齿被烟熏的很黄,还镶了一颗金牙,他又黑又瘦,好像一只猴子,可是他的眼睛却像蛇的眼睛一样,充满了阴冷,死死的盯住了我们这个团的人,好像盯住了猎物一样。

  本来我是还要在泰国好几天,可是突然接到电话说我之前一直负责联系的一个大客户和我们公司的业务突然有了起色,说不定谈成后我还会因此升职,所以我第二天就马不停蹄的回国了。

回国后还没顾上睡觉就忙了一天,忙的时候还不觉得饿,一闲下来肚子就咕咕叫了。

马上去楼下饭馆点了几个硬菜犒劳自己,可是我吃着却一点味道也没有,我还以为是太忙了,可是第二天吃东西还是一点胃口都没有,同时心中越发想念那天在泰国吃得黑红色的料理,到了后来几乎是像吸毒一样难以控制,这种反常的情况,让我不由得思考自己是不是像网络中一样,中了所谓的降头....没想到的是泰国下降头真实事件竟然真的被自己撞到了,简直比泰国皮皮岛灵异事件还可怕。

↓↓↓

真实的塞班岛灵异事件,夫妻旅游被二战日军鬼魂缠身 邪门

塞班岛是一个旅游的好地方,世界各地很多人都慕名前往这里度假。

了解历史的应该都知道,塞班岛在二战时期是日军的重要军事基地,这里曾经死过很多人。

因此也经常有游客在塞班岛旅游遇见灵异事件,今天小编要说的一件真实的塞班岛灵异事件发生在一对新婚夫妻身上,下面一起来看看吧。

真实的塞班岛灵异事件旅行团独独把我倆的名字给漏掉了,我们才找到领队解决这个事情。

坐了一夜的飞机,好不容易到了酒店,我们的蜜月房间竟然是两张单人床的标间形式!当时我就和旅行团的导游吵了一架,因为和我们当初预定的标准差太远了。

我竟然又莫的害怕起来,忽然觉得那个牌位好像和不远处山坡下的游人是两个完全不同的时空,那个牌位附近似乎太过安静了,靜的可怕,于是停留了一小会,我就赶紧往山坡下回。

可是大家都说来都来了,还是要玩一下拖伞才有意思,不会出问题。

于是我还是和老公一起上了拖伞,等到拖伞再次升高后第二次贴到海面时,我们就再也没有被拖起来,因为快艇的马达坏了没动力了!然后有那么十几秒时间我已经完全不知道状况迅速往下沉,海水灌满我的耳朵和鼻子眼睛,一瞬间我似乎觉得自己快要死了。

加入收藏
               

C#事件具体实现步骤

点击下载文档

格式为doc格式

  • 账号登录
社交账号登录