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. C++中int与string的转化

    C++中int与string的转化 int本身也要用一串字符表示,前后没有双引号,告诉编译器把它当作一个数解释.缺省情况下,是当成10进制(dec)来解释,如果想用8进制,16进制,怎么办?加上前缀, ...

  2. 【安卓本卓】Android系统源码篇之(一)源码获取、源码目录结构及源码阅读工具简介

    前言        古人常说,“熟读唐诗三百首,不会作诗也会吟”,说明了大量阅读诗歌名篇对学习作诗有非常大的帮助.做开发也一样,Android源码是全世界最优秀的Android工程师编写的代码,也是A ...

  3. Android--MP3播放器MediaPlayer

    前言 Android提供了常见的音频.视频的编码.解码机制.借助于多媒体类MediaPlayer的支持,开发人员可以很方便在在应用中播放音频.视频.本篇博客主要讲解在Android平台下如何播放一个音 ...

  4. 在Mac电脑编译c51程序

    如果不是Seven问起来,我以为C51这种东西已经属于历史遗迹了.不过简单搜索了一下,发现c51老而弥坚,仍然茁壮的生长着.原因据说,一方面是有很大的用户群和既有的软硬件资源,另外一方面,的确在很多的 ...

  5. 看板中的WIP限制

    WIP限制并不是真的要限制你的进度,事实上正相反. 什么是WIP限制? 在敏捷开发中,WIP限制决定了每种情况下的工作流中可以存续的最大工作量.限制进行中的工作数量可以更容易辨识团队工作流中的无效工作 ...

  6. PHP大法

    Topic Link http://ctf5.shiyanbar.com/DUTCTF/index.php Notes: 1) 进去发现 根据提示查看是否存在.txt文件,打开之后发现有东西 2)分析 ...

  7. LeetCode专题-Python实现之第21题:Merge Two Sorted Lists

    导航页-LeetCode专题-Python实现 相关代码已经上传到github:https://github.com/exploitht/leetcode-python 文中代码为了不动官网提供的初始 ...

  8. Centos7-yum部署配置LNMP+php-fgm,一台机器上部署

    一.简介 1.了解nginx特性 请参考,https://www.cnblogs.com/zhangxingeng/p/10150955.html 2.LNMP:linux+nginx+mysql+p ...

  9. webpack4.0各个击破(7)—— plugin篇

    webpack作为前端最火的构建工具,是前端自动化工具链最重要的部分,使用门槛较高.本系列是笔者自己的学习记录,比较基础,希望通过问题 + 解决方式的模式,以前端构建中遇到的具体需求为出发点,学习we ...

  10. WebBrowser Cookie

    WebBrowser的Cookie操作 .在WebBrowser中获取Cookie CookieContainer myCookieContainer = new CookieContainer(); ...