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 ...
随机推荐
- Add Digits 解答
Question Given a non-negative integer num, repeatedly add all its digits until the result has only o ...
- 微型 Python Web 框架 Bottle - Heroin blog
微型 Python Web 框架 Bottle - Heroin blog 微型 Python Web 框架 Bottle
- 在国内使用cnpm代替npm
npm是Node.js的模块依赖管理工具,由于使用npm安装包是从国外服务器下载,在国内很容易受到网络的影响,速度非常慢,因此可以选用cnpm.cnpm可以使用淘宝团队提供的淘宝npm镜像,你可以用此 ...
- 被Oracle全局暂时表坑了
今天凌晨4点多钟,在客户现场的负责人打电话给我,说非常奇怪,下载功能时快时慢.此下载功能非常复杂,之前一直是我优化,在半梦半醒中打开电脑,通过远程看着现场同事在PL/SQL developer中操作. ...
- javascript 判断系统设备
<script> function detectOS() { var sUserAgent = navigator.userAgent; var isWin = (navigator.pl ...
- [网络流最大流经典][uva 11082][矩阵解压]
题目大意 分析 #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring ...
- 虚拟化之KVM的安装续篇
介于上篇文章用的网络的方式安装系统并且磁盘文件格式为raw,不支持快照,所以再写下这篇文章,方便后续查看. 目的:通过nfs安装系统,磁盘文件格式为qcow2(支持快照). 如下操作和上篇文章只有部分 ...
- 手工启动oracle EM
在WINDOWS上安完ORACLE发现没有EM没有启动,在网上找了一个手工启动的方法,试了,在WIN下同样可用. 人家的原文如下: oracle@linux:~> sqlplus/ as sys ...
- HDU 5793 - A Boring Question
HDU 5793 - A Boring Question题意: 计算 ( ∑(0≤K1,K2...Km≤n )∏(1≤j<m) C[Kj, Kj+1] ) % 1000000007=? (C[ ...
- Java IO之序列化
序列化机制是Java语言内建的一种对象持久化方式,可以很容易的在JVM中的活动对象和字节数组之间转换.它的一个重要用途就是远程方法调用的时候,用来对开发人员屏蔽底层实现细节(远端的开发人员不知道这个对 ...