文章目录:

     1、C#(.net framework框架)中的事件以及特点

   2、事件的组成部分

   3、编辑器如何实现事件的

        4、显式实现事件

1、C#(.net framework框架)中的事件以及特点

    CLR事件模型以委托为基础。使用委托来调用回调方法。声明方式使用event关键字。 事件可以理解为在CLR中使用event关键字修饰的一个委托实例。

  事件的特点如下所示:

   1)、方法能登记对事件关注

     2)、方法能注销对事件的关注

   3)、事件发生时,登记了的方法将收到通知

2、事件的组成部分

  1)、定义类型容纳所有需要发送给事件通知接收者的附加信息(事件要传递的参数,我是这么理解的)

     /// <summary>
/// 发送给事件接收者的附加信息
/// </summary>
public class DailyEventArgs : EventArgs
{
private readonly string _title;
private readonly string _body;
public DailyEventArgs(string title,string body)
{
this._title = title;
this._body = body;
}
public string Title { get {return _title; } }
public string Body { get { return _body; } }
}

发送给事件接收者的附加信息

这里解释下EventArgs,它由FCL中定义,根据CLR事件约定,所有事件参数均要继承自这个类,且事件的参数类名要以EventArgs结尾。它的定义如下:

  2)、定义事件成员

  事件成员使用event关键字修饰。我们需要定义事件的可访问级别(默认都是public);委托类型(要调用的方法的原型)。

 class DailiesEventDemo
{
//也可以这么定义
//public delegate void CustomDelegate(object sender, DailyEventArgs e);
//public event CustomDelegate CustomDaily; public event EventHandler<DailyEventArgs> NewDaily; }

事件成员

  NewDaily是事件的名称,这里使用系统提供的委托EventHandlerEventHandler的定义如下所示,所以我们可以接受的方法原型为void MethodName(Object sender,DailyEventArgs e);

  

  3)、定义负责引发事件的方法来通知事件的的登记对象

  按照CLR的约定,我们需要定义一个受保护的虚方法。引发事件时,调用该方法。方法的参数为我们定义的DailyEventArgs对象,方法的默认实现是检查是否有对象登记了对事件的关注,假如有对象登记对事件的关注,引发事件通知登记对象。

         protected virtual void OnDendDaily(DailyEventArgs e)
{
//为了线程安全,将委托字段存入一个临时变量中
EventHandler<DailyEventArgs> temp = Volatile.Read(ref NewDaily);
if (temp != null)
temp.Invoke(this, e);
}

定义负责引发事件的方法

  4)、定义方法将输入转化为期望事件

         /// <summary>
/// 定义方法将输入转化为期望事件
/// </summary>
/// <param name="title"></param>
/// <param name="body"></param>
public void SimulateNewDaily(string title,string body)
{
DailyEventArgs dailyEventArgs = new DailyEventArgs(title, body);
OnDendDaily(dailyEventArgs);
}

定义方法将输入转化为期望事件

然后让我们定义两个关注类,来登记下事件(C# 用+= 来登记对事件的关注,-=注销对事件的关注),具体代码如下:

  

     public class ZhangSan
{
public void ObtainNewDaily(object sender, DailyEventArgs e)
{
Console.WriteLine($"张三获取一份报纸,标题是{e.Title},内容是{e.Body}");
}
}
public class LiSi
{
public void ObtainNewDaily(object sender, DailyEventArgs e)
{
Console.WriteLine($"李四获取一份报纸,标题是{e.Title},内容是{e.Body}");
}
}
static void Main(string[] args)
{
try
{
DailiesEventDemo dailiesEventDemo = new DailiesEventDemo();
ZhangSan zhangSan = new ZhangSan();
dailiesEventDemo.NewDaily += zhangSan.ObtainNewDaily; //张三登记注册
LiSi liSi = new LiSi();
dailiesEventDemo.NewDaily += liSi.ObtainNewDaily; //李四登记注册
dailiesEventDemo.SimulateNewDaily("人民日报", "程序员的崛起");
dailiesEventDemo.NewDaily -= liSi.ObtainNewDaily; //李四取消登记注册
dailiesEventDemo.SimulateNewDaily("新华日报", "How find a gril friend for Procedures MonKey ");

事件执行

3、编辑器实现事件的方式

    我们用ILSpy来反编译下我们生成的程序集。

可以看到,public event EventHandler<DailyEventArgs> NewDaily; 被编译器翻译成了一个具有add 和remove方法的对象,add 和remove方法中的 EventHandler<DailyEventArgs> eventHandler = this.NewDaily;拿到当前事件(被event修饰的委托)对象,然后调用 Delegate.Combine() 和 Delegate.Remove()方法添加或者移除委托连中的委托。

4、显式实现事件

上面可以看到,每一个事件都会生成一个委托的实例字段(例如上面的NewDaily),在winform程序中,窗体控件(system.windows.forms.controller)具有70多个事件,为此我们需要准备70多个委托字段,然而我们经常用到的事件也就几个而已,这样就导致了大量的委托实例字段被创建,从而浪费大量内存。 好在我们可以显示实现事件:

 public sealed class EventKey { }
public sealed class EventSet {
//私有字典维护EventKey => delegate 的映射
private readonly Dictionary<EventKey, Delegate> m_events = new Dictionary<EventKey, Delegate>(); //添加EventKey => delegate映射 或者将委托和现有的EventKey合并
public void Add(EventKey eventKey,Delegate handler)
{
Monitor.Enter(m_events);
Delegate d;
m_events.TryGetValue(eventKey, out d);
m_events[eventKey] = Delegate.Combine(d, handler);
Monitor.Exit(m_events);
} //从EvetnKey 删除委托,并且在删除最后一个委托时候删除映射关系
public void Remove(EventKey eventKey, Delegate handler)
{
Monitor.Enter(m_events);
Delegate d;
if(m_events.TryGetValue(eventKey, out d))
{
d = Delegate.Remove(d, handler);
if (d != null)
m_events[eventKey] = d;
else
m_events.Remove(eventKey);
}
Monitor.Exit(m_events);
}
public void Raise(EventKey eventKey,Object sender,EventArgs e)
{
Delegate d;
Monitor.Enter(m_events);
m_events.TryGetValue(eventKey, out d);
Monitor.Exit(m_events);
if(d!=null)
{
d.DynamicInvoke(new Object[] { sender, e });
}
}
}
public class FooEventArgs : EventArgs { }
public class TryWithLostOfEvents
{
//定义私有实例字段来引用集合
//集合用于管理一组 事件/委托 对
//EventSet类型不是不是FCL的一部分,他是我自己的类型
private readonly EventSet m_eventSet = new EventSet();
//受保护属性识破爱生类能访问集合
protected EventSet EventSet { get { return m_eventSet; } }
// 支持Foo事件的代码,定义Foo事件的必要成员,构造静态制度对象标识事件;每个对象都有自己的哈希码,一边在对象的集合中查找这个事件的委托链表
protected static readonly EventKey s_fooEventKey = new EventKey();
//定义访问器方法,用于集合中CRUD
public event EventHandler<FooEventArgs> Foo
{
add { m_eventSet.Add(s_fooEventKey, value); }
remove { m_eventSet.Remove(s_fooEventKey, value); }
}
//调用事件的虚方法
protected virtual void OnFoo(FooEventArgs e)
{
m_eventSet.Raise(s_fooEventKey,this, e);
}
//定义将输入转换成这个事件的方法
public void SimulateFoo() { OnFoo(new FooEventArgs()); }
}

事件的显式实现

来运行一下

                 TryWithLostOfEvents tryWithLostOfEvents = new TryWithLostOfEvents();
tryWithLostOfEvents.Foo += TryWithLostOfEvents_Foo; //TryWithLostOfEvents_Foo登记对事件的订阅
tryWithLostOfEvents.Foo += fun1; // fun1登记对事件的订阅
tryWithLostOfEvents.SimulateFoo();
tryWithLostOfEvents.Foo -= fun1; // fun1注销对事件的订阅
tryWithLostOfEvents.SimulateFoo();

运行显示定义事件

浅析CLR的事件的更多相关文章

  1. 浅析CLR的异常处理模型

    文章目录: 异常概述 CLR中的异常处理机制 CLR中异常的核心类System.Exception类 异常处理的设计规范和最佳实践 异常处理的性能问题 其他拓展 1.异常概述 异常我们通常指的是行动成 ...

  2. 浅析mpvue的事件代理系统

    前言 说来惭愧,用 mpvue 大半年,小程序快一年了,居然还试图用 event.stopPropagation 方法阻止事件冒泡,也是有点蠢.痛定思痛,写篇博文来认真捋一捋小程序的事件系统和 mpv ...

  3. c#进阶之浅析委托和事件

    何为委托 加了delegate关键字,没有方法体{}的方法模版(方法列表);委托是一种类型 public void Write() { //TODO } //加上关键字delegate,去掉方法体{} ...

  4. 浅析CLR的GC(垃圾回收器)

    文章目录: 了解托管堆和GC GC高效的处理方式—代 特殊类型的清理 手动监控和控制对象生命周期 1.了解托管堆和GC 在面向对象环境中,每一个类型都代表了一种资源.我们要使用这些资源,就要为这些代表 ...

  5. 浅析 Nginx 网络事件

    Nginx 是一个事件驱动的框架,所谓事件主要指的是网络事件,Nginx 每个网络连接会对应两个网络事件,一个读事件一个写事件.在深入了解 Nginx 各种原理及在极端场景下的一些错误场景处理时,需要 ...

  6. [CLR via C#]11. 事件

    一. 设计要公开事件的类型 如果类型定义了事件成员,那么类型(或类型实例)就可以通知其他对象发生了一些特定的事情. 例如,Button类提供了一个名为Click的事件.应用程序中的一个或多个对象可能想 ...

  7. [CLR via C#]26. 计算限制的异步操作

    一.CLR线程池基础 前面说过,创建和销毁线程是一个比较昂贵的操作,太多的线程也会浪费内存资源.由于操作系统必须调度可运行的线程并执行上下文切换,所以太多的线程还有损于性能.为了改善这个情况,CLR使 ...

  8. NEsper使用的事件类型 z

    NEsper使用的事件类型来描述事件的类型信息.你的应用在启动时可能预先配置定义事件类型,或者在运行时通过API或EPL语法动态的增加事件类型. EPL中的create schema 的语法允许在运行 ...

  9. Wpf自定义路由事件

    创建自定义路由事件大体可以分为三个步骤: ①声明并注册路由事件. ②为路由事件添加CLR事件包装. ③创建可以激发路由事件的方法. 以ButtonBase类中代码为例展示这3个步骤: public a ...

随机推荐

  1. 【sqli-labs】 less28a GET- Blind based -All you Union&Select Belong to us -String -Single quote-parenthesis(GET型基于盲注的去除了Union和Select的单引号带括号字符型注入)

    和less28没什么区别,直接上个payload吧 http://192.168.136.128/sqli-labs-master/Less-28a/?id=0')%a0uNion%a0sElect% ...

  2. JDBC对MySQL数据库存储过程的调用

    一.MySQL数据库存储过程: 1.什么是存储过程 存储过程(英文:Stored Procedure)是在大型数据库系统中,为了完成特定功能而编写的一组的SQL语句集.存储过程经编译存储在数据库中,用 ...

  3. (C/C++学习)1.C++中vector的使用

    说明:vector是C++中一个非常方便的容器类,它用于存放类型相同的元素,利用成员函数及相关函数可以方便的对元素进行增加或删除,排序或逆序等等,下面将对这些功能一一叙述. 一.vector的第一种用 ...

  4. 51nod1596 搬货物

    现在有n个货物,第i个货物的重量是 2wi .每次搬的时候要求货物重量的总和是一个2的幂.问最少要搬几次能把所有的货物搬完. 样例解释: 1,1,2作为一组. 3,3作为一组. Input 单组测试数 ...

  5. Linux思维导图之计划任务

    查漏补缺,理解概念,及时总结,互相交流,欢迎拍砖. 用yum install -y vixie-cron这个命令进行安装计划任务服务,可以在安装之前使用crontab -e进行检测一下,服务器是否安装 ...

  6. 使用VirtualBox实现端口转发,以SSH与Django为例

    先来认识几个概念 (1)IP地址:又称为互联网协议地址,是计算机的物理地址,相当于计算机的编号,是32位的二进制数,通常被分割成4个8位的二进制数: (2)端口:指设备与外界通讯的接口,一台计算机的端 ...

  7. 2.SpringBoot的properties的属性配置详解

    SpringBoot是为了简化Spring应用的创建.运行.调试.部署等一系列问题而诞生的产物,自动装配的特性让我们可以更好的关注业务本身而不是外部的XML配置,我们只需遵循规范,引入相关的依赖就可以 ...

  8. 如何彻底卸载系统自带的IE浏览器

    IE浏览器是windows系统上自带的浏览器,有时我们想要用其他的浏览器,例如chrome,卸载IE浏览器,那么应该如何卸载呢?下面就以win7上的IE9为例,告诉大家如何卸载IE浏览器. 方法/步骤 ...

  9. ZOJ 2699 Police Cities

    Police Cities Time Limit: 10 Seconds      Memory Limit: 32768 KB Once upon the time there lived a ki ...

  10. (22)Spring Boot 拦截器HandlerInterceptor【从零开始学Spring Boot】

    上一篇对过滤器的定义做了说明,也比较简单.过滤器属于Servlet范畴的API,与Spring 没什么关系.     Web开发中,我们除了使用 Filter 来过滤请web求外,还可以使用Sprin ...