事件:

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

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

先来看看事件的特征: ?发行者确定何时引发事件,订户确定执行何种操作来响应该事件 ?一个事件可以有多个订户。一个订户可处理来自多个发行者的多个事件 ?没有订户的事件永远不会被调用 ?事件通常用于通知用户操作 ?如果一个事件有多个订户,当引发该事件时,会同步调用多个事件处理程序 ?支持异步调用 ?可以利用事件同步线程 ?在 .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. 设置cell背景色和选中色

    // 设置cell的背景色 UIView *bg = [[[UIView alloc] init] autorelease]; bg.backgroundColor = [UIColor colorW ...

  2. 结合Wireshark捕获分组深入理解TCP/IP协议栈之TCP协议(TCP报文格式+三次握手实例)

    摘要:     本文简单介绍了TCP面向连接理论知识,详细讲述了TCP报文各个字段含义,并从Wireshark俘获分组中选取TCP连接建立相关报文段进行分析. 一.概述     TCP是面向连接的可靠 ...

  3. 微服务API模拟框架frock介绍

    本文来源于我在InfoQ中文站翻译的文章,原文地址是:http://www.infoq.com/cn/news/2016/02/introducing-frock Urban Airship是一家帮助 ...

  4. 开源分享三(炫酷的Android Loading动画)

    开源分享三(炫酷的Android Loading动画) 分享GitHub上的一些Loading,为了提升产品用户体验,一个好的Loading必然是不可缺少的,对于一些耗时需要用户等待的页面来说会转移用 ...

  5. CentOS 7 下使用yum安装MySQL5.7.20 最简单 图文详解

      原文地址:https://blog.csdn.net/z13615480737/article/details/78906598  CentOS7默认数据库是mariadb, 但是 好多用的都是m ...

  6. mybatis-generator + mysql/ptsql

    用了mybatis-generator,我就不再想用注解了,这与我之前说的注解与XML并用是矛盾的,知识嘛,本来就是多元化的,今天喜欢这个,明天喜欢那个,哈哈,看了mybatis-generator下 ...

  7. Android 动态改变高度以及计算长度的EditText

    前段时间项目需求,需要做一个有限制长度的输入框并动态显示剩余文字,同时也要动态改变EditText的高度来增加用户体验.现整理出来与大家分享. 先来看看效果图 看了效果就分享一下布局 <Rela ...

  8. LeetCode -- 删除链表中值为k的元素

    本题目比較直接,一次遍历遇到匹配的元素直接删除(通过n.next = n.next.next)就能够了,仅仅是须要考虑到:1.首节点的情况2.末节点的情况 下面为实现: public ListNode ...

  9. [React Router v4] Style a Link that is Active with NavLink

    We often need to be able to apply style to navigation links based on the current route. In React Rou ...

  10. 小强的HTML5移动开发之路(29)—— JavaScript回顾4

    一.变量的作用域 javascript脚本的执行过程分为两个阶段: 第一阶段,js引擎()先扫描整个javascript代码.当碰到<script>时,会先创建一个全局的活动对象,将< ...