C#基础:事件(一) 【转】
前面简要介绍了委托的基本知识,包括委托的概念、匿名方法、Lambda表达式等,现在讲讲与委托相关的另一个概念:事件。
事件由委托定义,因为事件的触发方(或者说发布方)并不知道事件的订阅方会用什么样的函数名称,这个函数名称由订阅方自己决定。假如不这样做,那么事件的订阅方必须公开一个专门用于处理事件的函数给事件触发方,由触发方在事件触发的时候调用这个函数。这样一来,触发方必须知道订阅方的细节,才能有效地触发事件,显然这是不合理的,触发方与订阅方耦合性太大了,不具备通用性。
事实上,事件的触发方只需要确定好事件处理函数的签名即可。也就是说,触发方只需要定义在事件发生时需要传递的参数,而在订阅方,只需要根据这个签名定义一个处理函数,然后将该函数“绑定”到事件列表,就可以通过签名中的参数,对事件做相应的处理。定义函数签名非常简单,就是使用委托。下面我们来简单看一个例子。这个例子模拟一个服务器程序,它有Start和Stop两个操作,分别表示启动和停止服务。在成功启动以及成功停止时,都会触发一个“成功”的事件,并公布事件发生的确切时间。
一、定义函数签名(委托)
其实对于我们的例子,事件处理函数的签名中只需要一个参数,就是事件发生的确切时间。因此在定义委托的时候,只需要定义一个时间(DateTime)类型的参数即可。为了能够让我们的程序看上去更加标准,并且为了后面描述的方便,我们还是将这个参数封装在一个类里,并且该类继承于System.EventArgs类。
- public class ServerEventArgs : System.EventArgs
- {
- #region Public Properties
- /// <summary>
- /// 读取或设置服务器事件发生的时间
- /// </summary>
- public DateTime FireDateTime { get; set; }
- #endregion
- #region Constructors
- /// <summary>
- /// 默认构造函数,使用当前时间作为服务器事件
- /// 发生的时间
- /// </summary>
- public ServerEventArgs()
- {
- this.FireDateTime = DateTime.Now;
- }
- /// <summary>
- /// 使用给定的事件作为服务器事件发生的时间并
- /// 对参数对象进行初始化
- /// </summary>
- /// <param name="fireDateTime"></param>
- public ServerEventArgs(DateTime fireDateTime)
- {
- this.FireDateTime = fireDateTime;
- }
- #endregion
- }
现在来定义这个委托:
- public delegate void ServerEventHandler(object sender, ServerEventArgs e);
委托的第一个参数表示事件将由谁来触发(谁是事件的发布者),而第二个参数则是我们刚刚定义的事件参数,它只有一个属性,就是事件的触发时间。函数签名(委托)已经定义好了,接下来需要对事件进行定义。
二、定义事件
在C#中,使用event关键字定义事件。事件定义的形式是:“<modifier> event <event_handler> name”,其中modifier是大家熟知的访问修饰符,也就是“public”、“protected”等,event_handler是定义了事件处理函数签名的委托(在本例中,也就是上面的ServerEventHandler),name自然就是这个事件的名称了。由此,我们的两个事件(成功启动事件和成功停止事件)可以定义如下:
- /// <summary>
- /// 定义一个事件,当服务器正常启动后,触发该事件
- /// </summary>
- public event ServerEventHandler Started;
- /// <summary>
- /// 定义一个事件,当服务器正常结束后,触发该事件
- /// </summary>
- public event ServerEventHandler Stopped;
三、触发事件
事件当然由其发布者触发。考察服务器“Server”这个对象,启动和停止是其本身应有的操作,因此,启动与停止是否成功,也就只有它自己知道。那么成功启动与成功停止的事件自然由其自身引发。事件的触发可以在对象中的任何地方发生,比如在本例中,我们可以在Start方法最后部分调用Started事件,而在Stop方法的最后部分调用Stopped事件。从扩展性方面考虑,我们还是把事件的触发单独放到一个protected方法中,这样做的好处是,当我们对“Server”进行扩展的时候,我们还可以重写这个protected方法,以便在事件触发之前再进行其它特殊的操作。因此,我们的事件触发部分就实现如下:
- protected virtual void DoStarted(object sender, ServerEventArgs e)
- {
- if (Started != null)
- Started(sender, e);
- }
- protected virtual void DoStopped(object sender, ServerEventArgs e)
- {
- if (Stopped != null)
- Stopped(sender, e);
- }
- /// <summary>
- /// 执行服务器的启动操作
- /// </summary>
- public void Start()
- {
- // TODO: 在此启动服务器
- DoStarted(this, new ServerEventArgs(DateTime.Now));
- }
- /// <summary>
- /// 执行服务器的停止操作
- /// </summary>
- public void Stop()
- {
- // TODO: 在此停止服务器
- DoStopped(this, new ServerEventArgs(DateTime.Now));
- }
由此,服务器在成功启动后,就会调用DoStarted方法,进而触发Started事件;服务器的停止操作也与此类似。我们需要注意到DoStarted和DoStopped方法中的条件判断语句,该语句是用来检查Started和Stopped事件列表中是否有订阅,如果有则触发事件。这种做法是很有必要的,因为并非所有的访问者都会去订阅事件。
四、订阅事件
在订阅者内订阅事件,只需要将事件处理函数添加到相应的事件中即可。在C#中使用“+=”运算符将事件处理函数添加到事件,而使用“-=”运算符将事件处理函数从事件中删除。下面的代码初始化了一个服务器,订阅了该服务器的成功启动与成功停止事件,并试图启动和停止服务。我们可以看到,在服务器成功启动和成功停止完成后,系统会输出启动或停止的具体时间。
- class Program
- {
- static void Main(string[] args)
- {
- Server server = new Server();
- // 订阅成功启动的事件
- server.Started += new Server.ServerEventHandler(server_Started);
- // 订阅成功停止的事件
- server.Stopped += new Server.ServerEventHandler(server_Stopped);
- // 启动服务
- server.Start();
- // 休息3秒钟,以模拟服务的处理时间
- Thread.Sleep(3000);
- // 停止服务
- server.Stop();
- }
- static void server_Stopped(object sender, ServerEventArgs e)
- {
- Console.WriteLine("Server successfully stopped at: {0}", e.FireDateTime);
- }
- static void server_Started(object sender, ServerEventArgs e)
- {
- Console.WriteLine("Server successfully started at: {0}", e.FireDateTime);
- }
- }
本文简要介绍了事件的概念、事件的定义、触发以及订阅的相关内容。有关事件的其它内容,比如EventHandler和EventHandler<T>委托、事件处理函数列表、接口内事件的实现等,将在后续的文章中一一介绍。
本文相关演示源代码:点击下载此文件
C#基础:事件(一) 【转】的更多相关文章
- 【深入浅出Linux网络编程】 “基础 -- 事件触发机制”
回顾一下“"开篇 -- 知其然,知其所以然"”中的两段代码,第一段虽然只使用1个线程但却也只能处理一个socket,第二段虽然能处理成百上千个socket但却需要创建同等数量的线程 ...
- 第一百六十九节,jQuery,基础事件
jQuery,基础事件 学习要点: 1.绑定事件 2.简写事件 3.复合事件 JavaScript 有一个非常重要的功能,就是事件驱动.当页面完全加载后,用户通过鼠标 或键盘触发页面中绑定事件的元素即 ...
- C#基础---事件的使用
一:什么是事件 事件是可以被控件识别的操作,如按下确定按钮,选择某个单选按钮或者复选框.每一种控件有自己可以识别的事件,如窗体的加载.单击.双击等事件,编辑框(文本框)的文本改变事件,等等.事 ...
- 9、网页制作Dreamweaver(jQuery基础:事件)
事件 定义 即当HTML中发生某些事(点击.鼠标移过等)的时候调用的方法 $(selector).action() 触发 事件的触发有两种方法: 1.直接将事件click写在<javascrip ...
- JS基础——事件绑定
上一篇博客JS事件对象中,老师问JS事件处理和VB中的事件处理有什么联系?先来解决一下这个问题.举个VB.net中事件处理的样例(JS敲久了,VB习惯的都不熟悉了,看来得常常回想了): 1.事件处理V ...
- javascript基础-事件1
原理 事件分两种.第一种浏览器事件,由浏览器抛出事件,它是人机交互的基础:第二种自定义事件,由程序员抛出事件,它是模拟事件流程.两者都是为了完成数据的传递. 浏览器事件 机制 冒泡和捕获两种机制.因I ...
- javascript基础-事件2
DOM0,DOM2,DOM3事件类型 图解: 范畴 响应顺序(标:标准浏览器.IE9+) 注意点 MouseEvent 标: mousedown-mouseup-click-mousedown-mou ...
- jQuery事件篇---基础事件
写在前面: 有一段时间未更新博客了,利用这段时间,重新看了<jQuery基础教程 第四版>和<锋利的jQuery 第二版>,这两本书绝对是jQuery入门非常好的书,值得多读几 ...
- C#基础-事件 继承类无法直接引发基类的事件
An event can be raised only from the declaration space in which it is declared. Therefore, a class c ...
- jquery基础事件
一.常用的事件有:click.dblclick. mousedown.mouseup.mousemove.mouseover.mouseout.change.select.submit.keydown ...
随机推荐
- Linux中shell文件操作大全
1.创建文件夹#!/bin/shmkdir -m 777 "%%1" 2.创建文件#!/bin/shtouch "%%1" 3.删除文件#!/bin/shrm ...
- [破解] DRM-内容数据版权加密保护技术学习(中):License预发放实现
在上一篇文章里实现了对媒体文体的DRM加密,现在一起来实现License的预发放. 所谓预发放就是在播放媒体文件之前先获取到License,License获取成功后,可直接在电脑上进行媒体文件的播放. ...
- wxPython Major类
转载自:http://www.yiibai.com/wxpython/wxpython_major_classes.html 原始的 wxWidgets(用C++编写)是一个巨大的类库.GUI类从 ...
- C++ Primer 有感(异常处理)
1.异常是通过抛出对象而引发的.该对象的类型决定应该激活哪个处理代码.被选中的处理代码是调用链中与该对象类型匹配且离抛出异常位置最近的那个. 2.执行throw的时候,不会执行跟在throw后面的语句 ...
- Qemu之Network Device全虚拟方案三: I/O虚拟化
前面两文主要对前端网络流的数据路径和虚拟网卡的创建进行了说明,这些能够看做是Guest OS网络数据包收发的准备工作,那么网络数据包是怎样在Guest OS中进进出出的呢,本文就是重点讲述Guest ...
- Android应用程序组件Content Provider在应用程序之间共享数据的原理分析
文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6967204 在Android系统中,不同的应用 ...
- unity tips
1.在unity 的mecanim中,如果一个动画指向两个或两个以上的动画,那么在inspector中,transitions中可以看到所有的过渡路径,这些路径是有先后顺序的.
- linux下Mysql 的安装、配置、数据导入导出
MySQL是一种开放源代码的关系型数据库管理系统(RDBMS),虽然功能未必很强大,但因它的免费开源而广受欢迎. 这次,接着上一篇<CentOs minimal安装和开发环境部署>,讲下L ...
- exchange邮箱的”单点登陆“
在跟exchange集成登陆时,通常有这样的需求,用户需要点击邮件链接的时候直接打开,不再需要输入用户名密码,实现所谓的单点登陆. 通常有两种方式 1.form认证 登陆原理:用js模拟表单登陆 代码 ...
- <audio>使用2
1.属性测试 <!--显示控件--> <audio src="../images/wind.mp3" id="audioOne" contro ...