1. 简介

  1. 事件是一种类型安全的委托(具体实例说明见《精通C#》 --10.5 C#事件)

    为什么这么说呢?可以类比属性和字段,属性是对字段的封装,其实 事件也就是封装了一个委托。

    但是你要知道:委托是一种自定义的数据类型,事件只是一种类的成员。


  1. 那么现在你要问:事件到底有什么具体的作用?

    事件呢?就是有一段代码(方法)触发(raise)事件,事件被触发后则订阅事件的事件处理程序(event handler) 就会开始执行。

    从这点看,事件类似异常,因为他们都是由对象引发的,并且可以由我们提供的代码处理


  2. 订阅一个事件的含义:是提供代码在事件发生时执行这些代码,这些代码称为事件处理程序

    订阅事件的语法如下:

    事件名+=new 委托名(方法名);

    事件名+=函数名

    注意订阅事件的可以是委托对象,也可以直接是一个函数或是匿名函数或Lambda表达式

    只要这个函数的返回值类型和签名与事件一致即可


  3. 我们都知道委托是类似c语言中的函数针,是一种函数指针在面向对象中的封装,是一种函数回调机制(回调函数就是一通过函数指针调用函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的数时,我们就说这是回调函数。);

    事件是用户与应用程序交互的基础,它是回调机制的一种应用。

    简而言之:委托的本质是引用类型,用包装回调函数,用于实现回调机制。事件的本质是一个类型安全的(多播)委托,事件是回调机制的一种应用。


  4. 事件和委托的关系

    事件的委托类似属性和字段,事件是对委托的封装,这句话意思就是:事件是一类型安全的委托

    事件比委托有更多的限制

    1、事件只能同“+=”和“-=”来绑定方(在事件中这个方法叫事件的处理程序,其实这这种绑定方法就是多播委托的绑定方法

    2、只能在类的内部调用(触发)事件,但是委托就可以在类外调用函数那样调用



2.实际案例

2.1实际案例1

代码实际背景:

当裁判的发令枪响起,触发事件,事件触发执行的动作就是运动员跑起来了

裁判是发布者,触发事件的方法是发令枪响,

运动员是事件的订阅者,事件发生后就开跑。

    class Program
{
static void Main(string[] args)
{
//实例化事件发布者
Judgment judgment = new Judgment();
//实例化事件订阅者
RunSporters runsporter = new RunSporters(); // 订阅者类RunSporters中的方法Run()和RunFail()订阅事件eventRun
//通过委托订阅
judgment.eventRun += new Judgment.delegateRun(runsporter.Run);
//通过函数订阅
judgment.eventRun += runsporter.RunFail; //执行引发事件的方法,这句代码之后事件的订阅者中的事件处理程序Run()和RunFail()开始运行
judgment.Begin(); Console.ReadKey();
}
} //事件发布者,也称发布器(publisher)
//发布器(publisher) 是一个包含事件和委托定义的对象。事件和委托之间的联系也定义在这个对象中。
//发布器(publisher)类的对象调用这个事件,并通知其他的对象。
class Judgment
{ //定义一个委托(你要明白:这个委托类型就是事件处理程序的函数类型)
public delegate void delegateRun(); //定义一个事件
//事件的声明与之前委托变量delegate1的声明唯一的区别是多了一个event关键字,且委托写“()”,事件不需要
public event delegateRun eventRun; //引发事件的方法
//当方法Begin()被执行了,此时就会触发事件 eventRun
public void Begin()
{
if (eventRun != null)
{
//被引发的事件
eventRun();
//☆☆☆☆☆☆☆☆☆☆☆☆
//注意这里就是体现“事件只能在类的内部调用”的地方,你在这个发布者类之外不能调用事件eventRun
} }
} //事件订阅者,也称订阅器(subscriber)
//订阅器(subscriber) 是一个接受事件并提供事件处理程序的对象。
//在发布器(publisher)类中的委托调用订阅器(subscriber)类中的方法(事件处理程序)。
//一个事件可以有多个订阅者,事件的发布者也可以是事件的订阅者。
public class RunSporters
{
//事件处理程序
public void Run()
{
Console.WriteLine("运动员跑起来了");
} //事件处理程序
public void RunFail()
{
Console.WriteLine("有一个起跑失败");
}
}



2.2实际案例2--带有参数的事件

代码实际背景:

热水器仅仅负责烧水,有一个温度字段temperature ,它不能发出警也不能显示水温;

在水烧开时由警报器发出警报;

显示器显示提示和水温。

观察者模式:

Subject:监视对象,它往往包含着其他对象所感兴趣的内容。

在本范例中,热水器就是一个监视对象,它包含的其他对象所感兴趣的内容,就是temprature字段,当这个字段的值快到100时,会不断把数据发给监视它的对象。

Observer:监视者,它监视Subject,当Subject中的某件事发生的时候,会告知Observer,而Observer则会采取相应的行动。

在本范例中,Observer有警报器和显示器,它们采取的行动分别是发出警报和显示水温。

在本例中,事情发生的顺序应该是这样的:

警报器和显示器告诉热水器,它对它的温度比较感兴趣(注册)。

热水器知道后保留对警报器和显示器的引用。

热水器进行烧水这一动作,当水温超过95度时,通过对警报器和显示器的引用,自动调用警报器的MakeAlert()方法、显示器的ShowMsg()方法。


//发布者--热水器
public class Heater
{
private int temperature;
public delegate void BoilHandler(int param); //声明委托
public event BoilHandler BoilEvent; //声明事件 // 烧水
public void BoilWater()
{
for (int i = 0; i <= 100; i++)
{
temperature = i; //当温度大于95度开始触发事件
if (temperature > 95)
{
//如果有对象注册
if (BoilEvent != null)
{
BoilEvent(temperature); //调用所有注册对象的方法(事件处理程序)
}
}
}
}
} //订阅者--显示器
public class Display
{
//静态方法
public static void ShowMsg(int param)
{
Console.WriteLine("Display:水快烧开了,当前温度:{0}度。\n", param);
}
} // 订阅器--- 警报器
public class Alarm
{
public void MakeAlert(int param)
{
Console.WriteLine("Alarm:嘀嘀嘀,水已经{0} 度了:", param);
}
} class Program
{
static void Main(string[] args)
{
//注意我们不需要实例化Display类,为什么呢?因为我们只是使用它里面的一个静态方法,直接使用类名点
Heater heater = new Heater();
Alarm alarm = new Alarm(); //注册方法(订阅事件)
//法1
heater.BoilEvent += new Heater.BoilHandler(alarm.MakeAlert); //法2
heater.BoilEvent += (new Alarm()).MakeAlert; //给匿名对象注册方法 heater.BoilEvent += Display.ShowMsg; //注册静态方法,静态类中的方法调用直接使用类名点 heater.BoilWater(); //触发事件,会自动调用注册过对象的方法 Console.ReadKey();
}
}



3.标准事件的用法

C#的BCL(基础类库)中针对事件定义的时候所需要的委托有一个标准委托

public delegate void EventHandler(Object sender, EventArgs e);

其中:

  1. 第一个参数sender 用来保存触发事件的对象的引用。由于是Object类型的,所以可以匹配任何类型的实例。

  2. 第二个参数用来保存有该事件相关的信息



3.1通过扩展EventArgs来传递数据

注意到Eventhandler的第二个参数是EventArgs类型的,

那么我们来看看EventArgs类

public class EventArgs
{
public static readonly EventArgs Empty;
public EventArgs();
}

EventArgs被设计为不能传递任何数据,如果你想要传递数据怎么办?

我们可以通过扩展EventArgs来传递数据:也就是声明一个EventArgs的子类,使用合适的字段来保存需要传递的数据

比如,,我们要传递一个int类型的参数:

    public class ExtendEventArgs:EventArgs
{
public int Tem;
public ExtendEventArgs(int tem)
{
Tem=tem;
}
}

那么这时候我们就可以这样声明EventHandler委托

public delegate void EventHandler(Object sender,ExtendEventArgs e);

你要知道的是C#2.0中引入了EventHandler泛型委托:

所以你不需要因为修改参数而重新声明EventHandler委托

你直接这样定义事件:

public event EventHandler<ExtendEventArgs> MyEvent;



3.2代码实例

代码背景:

汽车销售类CarDealer和顾客类Consumer

CarDealer提供一个新车到达出发事件,Consumer类订阅该事件


class Program
{
static void Main(string[] args)
{
CarDealer dealer = new CarDealer();
Consumer consumer=new Consumer ("志铭"); dealer.NewCarEvent += consumer.ConsumerReply; dealer.RaiseNewCarInfo("BMW"); Console.ReadKey(); } //扩展EventArgs类,添加一个Car属性用于传递数据
public class CarInfoEventArgs : EventArgs
{
public CarInfoEventArgs(string car)
{
this.Car = car;
} public string Car;
} //发布类
public class CarDealer
{ //public delegate EventHandler(object sender, CarInfoEventArgs e);//声明委托EventHandler
//public event EventHandler NewCarEvent;//声明事件NewCarEvent //下面一行就可以代替上面注释的两行 //声明事件NewCarIfno
public event EventHandler<CarInfoEventArgs> NewCarEvent; //触发事件NewCarIfno的函数
public void RaiseNewCarInfo(string car)
{
Console.WriteLine($"CarDealer :new car {car}");
if (NewCarEvent != null)
{
NewCarEvent(this, new CarInfoEventArgs(car)); //注意:事件的参数类型,声明事件时所用的委托的参数类型和事件处理程序的参数类型三者一样 //注意这里的参数this
//你想想为什么写this?
//EventHandler委托的两个参数,第一个参数就是触发事件的事件对象
}
}
} //订阅类
public class Consumer
{
public string Name;
public Consumer (string name)
{
this.Name = name;
} //事件处理程序,注意参数和事件的委托EventHandler<CarInfoEventArgs>一样
public void ConsumerReply(object sender, CarInfoEventArgs e)
{
Console.WriteLine($"{this.Name }:car {e.Car } very good!");
}
} }

运行结果

CarDealer:new car BMW
志铭:car BMW very good!



4.示例源代码下载

示例源代码下载

C#-事件event的更多相关文章

  1. 事件EVENT与waitforsingleobject的使用

    事件event与waitforsingleobject的配合使用,能够解决很多同步问题,也可以在数据达到某个状态时启动另一个线程的执行,如报警. event的几个函数: 1.CreateEvent和O ...

  2. 经典线程同步 事件Event

    阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇 一个经典的多线程同步问题> <秒杀多线程第五篇 经典线程同步关键段CS> 上一篇中使用关键段来解决经典的多线程同步互斥问题 ...

  3. C#事件(event)解析

    事件(event),这个词儿对于初学者来说,往往总是显得有些神秘,不易弄懂.而这些东西却往往又是编程中常用且非常重要的东西.大家都知道windows消息处理机制的重要,其实C#事件就是基于window ...

  4. 【温故知新】c#事件event

    从上一篇文章[温故知新]C#委托delegate可知,委托delegate和事件Event非常的相似,区别就是event关键字,给delegate穿上了个“马甲”. 让我们来看官方定义: 类或对象可以 ...

  5. 事件[event]_C#

    事件(event): 1.       事件是类在发生其关注的事情时用来提供通知的方式.例如,封装用户界面控件的类可以定义一个在单击该控件时发生的事件.控件类不关心单击按钮时发生了什么,但它需要告知派 ...

  6. C#中的委托(Delegate)和事件(Event)

    原文地址:C#中的委托(Delegate)和事件(Event) 作者:jiyuan51 把C#中的委托(Delegate)和事件(Event)放到现在讲是有目的的:给下次写的设计模式--观察者(Obs ...

  7. MFC线程(三):线程同步事件(event)与互斥(mutex)

    前面讲了临界区可以用来达到线程同步.而事件(event)与互斥(mutex)也同样可以做到. Win32 API中的线程事件 HANDLE hEvent = NULL; void MainTestFu ...

  8. 重温委托(delegate)和事件(event)

    1.delegate是什么 某种意义上来讲,你可以把delegate理解成C语言中的函数指针,它允许你传递一个类A的方法m给另一个类B的对象,使得类B的对象能够调用这个方法m,说白了就是可以把方法当作 ...

  9. C#总结(二)事件Event 介绍总结

    最近在总结一些基础的东西,主要是学起来很难懂,但是在日常又有可能会经常用到的东西.前面介绍了 C# 的 AutoResetEvent的使用介绍, 这次介绍事件(event). 事件(event),对于 ...

  10. 多线程面试题系列(6):经典线程同步 事件Event

    上一篇中使用关键段来解决经典的多线程同步互斥问题,由于关键段的"线程所有权"特性所以关键段只能用于线程的互斥而不能用于同步.本篇介绍用事件Event来尝试解决这个线程同步问题.首先 ...

随机推荐

  1. Oracle常用语句

    Oracle数据库常用sql语句 ORACLE 常用的SQL语法和数据对象一.数据控制语句 (DML) 部分 1.INSERT (往数据表里插入记录的语句) INSERT INTO 表名(字段名1, ...

  2. mogodb分片配置

    下图展示了在MongoDB中使用分片集群结构分布: 上图中主要有如下所述三个主要组件: Shard: 用于存储实际的数据块,实际生产环境中一个shard server角色可由几台机器组个一个repli ...

  3. Django+Bootstrap+Mysql 搭建个人博客(三)

    3.1.分页功能 (1)views.py from django.core.paginator import Paginator,EmptyPage,PageNotAnInteger def make ...

  4. asp.net core系列 31 EF管理数据库架构--必备知识 反向工程

    一.   反向工程 反向工程是基于数据库架构,生成的实体类和DbContext类代码的过程,对于Visual Studio开发,建议使用PMC.对于其他开发环境,请选择.NET Core CLI工具( ...

  5. asp.net core 系列 5 MVC框架路由(上)

    一. 概述 介绍asp.net core路由时,我初步想了下,分几篇来说明.  路由的知识点很多,参考了官方文档提取出一些重要的知识点来说.    在ASP.NET Core中是使用路由中间件来匹配传 ...

  6. source map 的原理探究

    线上产品代码一般是编译过的,前端的编译处理过程包括不限于 转译器/Transpilers (Babel, Traceur) 编译器/Compilers (Closure Compiler, TypeS ...

  7. 环境与工具2:建立高效的mac环境

    你的工作与生活离不开电脑,电脑是一个工具,也是一个环境.环境是不是绿水青山,是不是得心应手,这是很重要的事情.小程平时使用macbook来学习跟娱乐,最近重装了系统,很多环境与工具都需要重新组建. 那 ...

  8. Web技术的发展 网络发展简介(三)

    在上一篇文章中,对TCP/IP通信协议进行了简单的介绍 通信协议是通信的理论基石,计算机.操作系统以及各种网络设备对通信的支持是计算机网络通信的物质基础 而web服务则是运行于应用层,借助于应用层的协 ...

  9. Service Worker MDN英文笔记

    前言: 以前学习基础知识的时候总看别人写的入门文章,但有时候还是一脸懵逼,直到自己用心阅读了MDN的英文文档才对基础知识的一些理论有了更深的理解,所以我在边阅读文档的时候边记录下帮助比较大的,也方便大 ...

  10. 关于HTML相关知识随记

    HTML是构成网页文档的主要语言,它由HTML标签和字符信息组成.HTML标签可以标识文字.图形.动画.声音.表格.超链接等网页对象,字符信息用以传达网页内容,如标题.段落文本.图像等. HTML4文 ...