事件:

"在发生其他类或对象关注的事情时,类或对象可以通过事件通知他们。发送(或引发)事件的类称为"发行者",接受(或处理)事件的类称为"订户"。"

上面这句话描述了事件的最本质功能,用于底层通知上层。正常的架构设计都是分层结构,而分层结构有一点很重要的就是底层对于上层的无知,当初这样设计是为了解耦,为了更好的面向对象,但是带来的问题是如何解决自下而上的信息流。因为自上而下的调用,我们通过接口就可以搞定一切了,上层可以看到下层提供的服务接口,那么正常的调用可以保证一路向下,底层调用中层提供的服务接口,中层的服务接口的实现中调用了底层的服务接口,这样感觉很是完美的设计模式。每一层都不再依赖彼此,隐藏了实现细节。但是现在遇到一个最简单的问题:如果需要底层来触发上层的行为,如何实现。很多程序员告诉我这个简单,轮询啊,底层不断轮询这一个事情的发生状况,如果发生了则启动一个线程专门去处理这个事情。这种解决方案只需要在底层多开出一个服务接口,该服务接口就是表示目前发生了什么事情,然后上层定时查看该接口,如果发生则采取相应操作。当然该种解决方案也是一种解决途径,但是估计你也觉得不好,第一无法实时,因为轮询,那么必定存在一个时差问题,也就是常说的响应时间问题。还有就是单独的轮询线程需要空间与时间的消耗。最让人郁闷还在于这个对于时空的消耗竟然与响应时间是反相关的,总之你想响应时间短,那么就意味着你不得不浪费大量时空,反之亦然。当然此种方法还要解决多线程冲突的问题,涉及到多线程冲突,锁解锁的问题,那么我觉得就不怕你的逻辑能力有多强,耐心有多大,随着项目规模的变大,线程的变多,你大脑崩溃那是早晚的事情。   此处我们引入事件模式。

先来看看事件的特征: ?发行者确定何时引发事件,订户确定执行何种操作来响应该事件 ?一个事件可以有多个订户。一个订户可处理来自多个发行者的多个事件 ?没有订户的事件永远不会被调用 ?事件通常用于通知用户操作 ?如果一个事件有多个订户,当引发该事件时,会同步调用多个事件处理程序 ?支持异步调用 ?可以利用事件同步线程 ?在 .NET Framework 类库中,事件是基于 EventHandler 委托和 EventArgs 基类的

C#类库中自带了一大堆事件,尤其那些控件。而对于我说到的这个底层触发上层的问题,那么绝大多数是需要自定义事件的。(库中自带事件的使用我就不讲了,如果这个你不会的话,未免对不起观众了。)所以下面就开始着重讲讲自定义事件的问题:

事件是类和对象向外界发出的消息,事件的执行是通过事件委托的方式,调用我们所准备好的处理方法。要响应某些事件并针对某些事件执行我们指定的方法,需要做到以下几步:

  • 声明委托、事件
///定义一个委托
public delegate void TestEventHandler(object sender, TestEventArgs e);
///用event关键字声明事件对象
public event TestEventHandler TestEvent;
  • 添加事件的触发方法,也就是通知接受者方法
//事件触发的方法
protected  void OnTestEvent(TestEventArgs e)
{
    if (TestEvent != null)
    {
        TestEvent(this, e);
    }
}

  

  • 添加事件引发方法
//引发事件的方法
public void RaiseEvent(char keyToRaiseEvent)
{
    TestEventArgs e = new TestEventArgs(keyToRaiseEvent);
    OnTestEvent(e);
}

  

  • 接受者处本地化响应方法
//定义本地处理事件的方法,他与声明事件的delegate具有相同的参数和返回值类型
public void KeyPressed(object sender, TestEventSource.TestEventArgs e)
{
    Console.WriteLine("发送者:{0},所按得健为:{1}", sender, e.KeyToRaiseEvent);
}

  

  • 接受者订阅事件
//订阅事件
public void Subscribe(TestEventSource evenSource)
{
    evenSource.TestEvent += new TestEventSource.TestEventHandler(KeyPressed);
}
 
//取消订阅事件
public void UnSubscribe(TestEventSource evenSource)
{
    evenSource.TestEvent -= new TestEventSource.TestEventHandler(KeyPressed);
}

  最终全部代码如下:

 
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5.  
  6. namespace ConsoleApplication1
  7. {
  8. /// <summary>
  9. /// 发布事件类
  10. /// </summary>
  11. public class TestEventSource
  12. {
  13. /// <summary>
  14. /// 定义事件参数类
  15. /// </summary>
  16. public class TestEventArgs : EventArgs
  17. {
  18. public readonly char KeyToRaiseEvent;
  19. public TestEventArgs(char keyToRaiseEvent)
  20. {
  21. KeyToRaiseEvent = keyToRaiseEvent;
  22. }
  23. }
  24.  
  25. ///定义一个委托
  26. public delegate void TestEventHandler(object sender, TestEventArgs e);
  27. ///用event关键字声明事件对象
  28. public event TestEventHandler TestEvent;
  29.  
  30. //事件触发的方法
  31. protected void OnTestEvent(TestEventArgs e)
  32. {
  33. if (TestEvent != null)
  34. {
  35. TestEvent(this, e);
  36. }
  37. }
  38.  
  39. //引发事件的方法
  40. public void RaiseEvent(char keyToRaiseEvent)
  41. {
  42. TestEventArgs e = new TestEventArgs(keyToRaiseEvent);
  43. OnTestEvent(e);
  44. }
  45. }
  46.  
  47. //监听事件类
  48. public class TestEventListener
  49. {
  50. //定义本地处理事件的方法,他与声明事件的delegate具有相同的参数和返回值类型
  51. public void KeyPressed(object sender, TestEventSource.TestEventArgs e)
  52. {
  53. Console.WriteLine("发送者:{0},所按得健为:{1}", sender, e.KeyToRaiseEvent);
  54. }
  55.  
  56. //订阅事件
  57. public void Subscribe(TestEventSource evenSource)
  58. {
  59. evenSource.TestEvent += new TestEventSource.TestEventHandler(KeyPressed);
  60. }
  61.  
  62. //取消订阅事件
  63. public void UnSubscribe(TestEventSource evenSource)
  64. {
  65. evenSource.TestEvent -= new TestEventSource.TestEventHandler(KeyPressed);
  66. }
  67.  
  68. }
  69.  
  70. class Program
  71. {
  72. static void Main(string[] args)
  73. {
  74. ///创建事件源对象
  75. TestEventSource es = new TestEventSource();
  76.  
  77. ///创建监听对象
  78. TestEventListener el = new TestEventListener();
  79.  
  80. ///订阅事件
  81. Console.WriteLine("订阅事件\t");
  82. el.Subscribe(es);
  83.  
  84. ///引发事件
  85. Console.WriteLine("输入一个字符,再按enter键");
  86. string str = Console.ReadLine();
  87. es.RaiseEvent(str.ToCharArray()[0]);
  88.  
  89. //取消订阅事件
  90. Console.WriteLine("\n取消订阅事件\n");
  91. el.UnSubscribe(es);
  92.  
  93. //引发事件
  94. Console.WriteLine("输入一个字符,再按enter健");
  95. str = Console.ReadLine();
  96. es.RaiseEvent(str.ToCharArray()[0]);
  97. Console.ReadLine();
  98.  
  99. }
  100. }
  101. }
 

赋值粘贴即可以执行,且看下面截图执行效果:

C#委托五(自定义事件)的更多相关文章

  1. C#:委托和自定义事件

    1. 委托概述 “委托”相当于C++中的“函数指针”,委托必须与所要“指向”的函数在“参数”和“返回类型”上保持一致; // 定义Person类 public class Person { publi ...

  2. C# 窗体间传值(使用委托与自定义事件)

    using System; using System.Drawing; using System.Windows.Forms; namespace 跨窗体调用控件 { public partial c ...

  3. 分享一个C#自定义事件的实际应用

    在C#.NET的开发中,事件是经常接触到的概念,比如为按钮添加点击事件,并写入点击按钮触发事件要运行的代码.不管是ASP.NET还是WinForm等各种形式的应用程序,最经常是为系统生成的事件写具体代 ...

  4. javaScript事件机制深入学习(事件冒泡,事件捕获,事件绑定方式,移除事件方式,阻止浏览器默认行为,事件委托,模拟浏览器事件,自定义事件)

    前言 JavaScript与HTML之间的交互是通过事件实现的.事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间.可以使用侦听器(或处理程序)来预订事件,以便事件发生时执行相应的代码.这种在传统软 ...

  5. jQuery 学习笔记(5)(事件绑定与解绑、事件冒泡与事件默认行为、事件的自动触发、自定义事件、事件命名空间、事件委托、移入移出事件)

    1.事件绑定: .eventName(fn) //编码效率略高,但部分事件jQuery没有实现 .on(eventName, fn) //编码效率略低,所有事件均可以添加 注意点:可以同时添加多个相同 ...

  6. Qt自定义事件的实现(转)

    原文:http://blog.csdn.net/michealtx/article/details/6866094 初学Qt,用了Qt自带的事件,然后想怎么才能定义自己的事件呢?又如何使用自定义事件呢 ...

  7. JS自定义事件之选项卡

    自定义事件是一种处理与DOM产生交互的代码逻辑片段之间耦合的很好的架构方法. 一个简单的jQuery插件——选项卡 让ul列表来响应点击事件.当用户点击一个列表项时,给这个列表项添加一个名为activ ...

  8. EventHandler委托与自定义委托

    http://blog.csdn.net/uuxyz/article/details/7175248 EventHandler委托与自定义委托 自定义委托: //1. public delegate ...

  9. C# 自定义事件(EventArgs)

    1,自定义事件: public class TextChangeEventArgs : EventArgs {     private string message;     public TextC ...

随机推荐

  1. 云服务器搭建 Nginx 静态网站

    第一步:安装 Nginx 在 CentOS 上,可直接使用 yum 来安装 Nginx(当然也可以通过下载压缩包.解压.编译的方式安装,不过太麻烦了) yum install nginx -y 第二步 ...

  2. (转)在server 2008R2组策略设置所有域计算机防火墙都处于更关闭状态

    组策略在域控中相当重要,我们可以下放一个组策略去统一管理下面客户端的配置,具体配置如下: 首先点击开始____管理工具____组策略管理 防火墙关闭完之后我们该如何到客户端验证呢? 首先我们需要现在客 ...

  3. mysql8 mongodb4 增删改查 性能对比,2019 最专业对比,nosql 真的比 sql 性能强很多?

    原文:mysql8 mongodb4 增删改查 性能对比,2019 最专业对比,nosql 真的比 sql 性能强很多? 版权所有:http://www.fengyunxiao.cn 近几年看了很多关 ...

  4. windows下perl使用

    windows下perl使用 windows下perl开发工具 Perl 的官方网址提供下载的windows perl开发环境 ActiveState Perl Strawberry Perl DWI ...

  5. php中usort自定义排序如何使用

    php中usort自定义排序如何使用 一.总结 一句话总结:多写一个规则函数,而这个函数的写法和普通函数一样,调用的时候规则函数用函数名的字符串. 1.用户自定义规则函数有哪三个? usort — 使 ...

  6. AOP 专题

    Spring框架有两个核心思想:IOC和AOP Spring IOC指的是控制翻转,使用普通JAVA Bean,在运行时由Spring容器统一管理和串联,服务于不同的流程,在开发过程中对Spring ...

  7. 《转》couldn&#39;t connect to server 127.0.0.1:27017 at src/mongo/shell/mongo.js:145

    couldn't connect to server 127.0.0.1:27017 at src/mongo/shell/mongo.js:145,有须要的朋友能够參考下. 应为昨天安装的时候没及时 ...

  8. Android 报错 Error:(303, 27) 错误: 找不到符号 符号: 方法 sin(float) 位置: 类 FloatMath

    今天更新了sdk,升级到Android SDK 23.发现Android studio用23编译 SlidingMenu时出错,错误如下: 报错的地方这这里: float distanceInflue ...

  9. php Apache配置伪静态的介绍

    以下是摘抄http://jingyan.baidu.com/article/86112f132aa7462737978718.html的,作为记录,方便以后参考 现有的在线网上视频教程对伪静态的讲解比 ...

  10. 设置好ftp后用xftp连接提示无法打开,无法显示远程文件夹

    原文:设置好ftp后用xftp连接提示无法打开,无法显示远程文件夹 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/the_victory/artic ...