一、初步了解事件

  事件是委托的一个子集,为了满足“广播/订阅”模式的需求而生。

  事件就是限制委托字段的包装器。限制外界对委托字段内部的访问。相当于封装。

事件就是能够发生的什么事情,主要有以下5个主体。

1、事件的拥有者(event source,对象)

2、事件的成员(event,成员)

3、事件的响应者(event subscriber,对象)

4、事件处理器(event hander,成员)——本质上是一个回调方法

5、事件订阅——把事件处理器与事件关联在一起,本质上是一种以委托类型为基础的约定。

事件不会主动发生,一定是被拥有者内部逻辑触发,才会发生。

  1.1事件订阅的写法

A、比较常用的订阅写法

this.myButton.Click += new System.EventHandler(this.myButton_Click);

也可以像下面这么写

this.myButton.Click +=this.myButton_Click

eventhander就是事件处理器,只不过可以省略,直接写方法名。

+=为事件订阅操作符

+=后面的就是事件的处理器,格式就是 事件 +=(订阅)xxxx(事件处理器)

B、另一种订阅方式的写法

这种写法直接使用了匿名方法,也就是方法只针对本次事件使用,并不复用。

拉姆达写法

简写

事件只能在+=或者-=的左边,其他的时候无法调用事件。

  1.2事件处理器

下面的代码是不是处理器?

protected virtual void OnPriceChanged(PriceChangedEventArgs e) {
if (PriceChanged != null) PriceChanged(this, e);
}

结论:是的,OnPriceChanged就是事件处理器。另外,ElapsedEventArgs和EventArgs 都是事件处理器,一个是timer elapse的处理器另一个是click处理器,你可以自定义事件处理器,比如我上面举的例子,自定义的事件处理器生命要加Protected,以限制访问级别。

闪电就是事件,扳手是属性,方块是方法。

事件处理器(event hander,成员)——本质上是一个回调方法

注意:

A事件处理器是方法成员

B挂接事件处理器的时候,可以使用委托实例,也可以直接使用方法名,这是个语法糖

C事件处理器对事件的订阅不是随意的,匹配与否由声明事件时所使用的委托类型来检测

D事件可以同步调用也可以异步调用

  1.3事件本身的声明

简略的声明格式:

字段式声明,field-like

public event OrderEventHandler Order;

完整的声明格式

private OrderEventHandler orderEventHandler;
public event OrderEventHandler Order
{
add
{
this.orderEventHandler += value;
}
remove
{
this.orderEventHandler -= value;
}
}

  1.4事件声明的语法糖

事件委托的声明有语法糖,就是system类下面的eventhandler这个方法,他是厂商给我们定义好的一个语法糖。

如果使用这个语法糖,就可以不用再去声明委托事件了,如下就可以不写:

public delegate void OrderEventHandler(Customer customer,OrderEventArgs e);

二、事件的应用

  2.1事件基本声明模式

  这种方式跟委托的写法比较相像,只不过在声明一个委托对象时加上event关键字。然后委托方法的后缀要加上eventhandler或者handler,至于为什么加?

A加eventhandler为了让所有人明确是为了给事件服务的

B加eventhandler为了表明这个委托是用来约束事件处理器的

C 委托创建的实例是用来存储事件的

//声明(事件)委托类型有点像方法,但是不要写错地方,委托(事件委托)是一种类,小心写成嵌套类型。
public delegate void PriceChangedHandler(decimal oldPrice, decimal newPrice);
//事件拥有者 event source
public class IPhone6
{
decimal price;
//事件的成员
public event PriceChangedHandler PriceChanged;
public decimal Price
{
get { return price; }
set
{
if (price == value) return;
decimal oldPrice = price;
price = value;
// 如果调用列表不为空,则触发。
if (PriceChanged != null)
PriceChanged(oldPrice, price);
}
}
}
class BasicStyle
{
static void Main(string[] args)
{
IPhone6 iphone6 = new IPhone6() { Price = };
//订阅事件 PriceChanged为事件,iphone6_PriceChanged是事件处理器
iphone6.PriceChanged += iphone6_PriceChanged;
// 调整价格(事件发生)
iphone6.Price = ;
Console.ReadKey();
}
//事件处理器 console是事件响应者
static void iphone6_PriceChanged(decimal oldPrice, decimal price)
{
Console.WriteLine("年终大促销,iPhone 6 只卖 " + price + " 元, 原价 " + oldPrice + " 元,快来抢!");
}
}

运行结果:

  有人可能会问,如果把上面的event关键字拿掉,结果不是一样的吗,到底有何不同?

  没错可以用事件的地方就一定可以用委托代替。

  但事件有一系列规则和约束用以保证程序的安全可控,事件只有 += 和 -= 操作,这样订阅者只能有订阅或取消订阅操作,没有权限执行其它操作。如果是委托,那么订阅者就可以使用 = 来对委托对象重新赋值(其它订阅者全部被取消订阅),甚至将其设置为null,甚至订阅者还可以直接调用委托,这些都是很危险的操作,广播者就失去了独享控制权。

  2.2 事件标准声明模式

  .NET 框架为事件编程定义了一个标准模式。设定这个标准是为了让.NET框架和用户代码保持一致。System.EventArgs是标准模式的核心,它是一个没有任何成员,用于传递事件参数的基类。

  利用System.EventArgs加上Eventhandler这个语法糖,形成了标准模式,按照标准模式,我们对于上面的iPhone6示例进行重写:

//传入的事件参数,派生自EventArgs类。EventArgs本身是一个委托。
public class PriceChangedEventArgs : System.EventArgs
{
public readonly decimal OldPrice;
public readonly decimal NewPrice;
public PriceChangedEventArgs(decimal oldPrice, decimal newPrice)
{
OldPrice = oldPrice;
NewPrice = newPrice;
}
}
//事件拥有者 event source
public class IPhone6
{
decimal price;
//使用了EventHandler,PriceChanged是事件本身,PriceChangedEventArgs是事件委托
public event EventHandler<PriceChangedEventArgs> PriceChanged;
//注意使用protected,事件处理器(其实事件委托和ONxxx都可以理解为事件处理器,都是在处理这个事件)
protected virtual void OnPriceChanged(PriceChangedEventArgs e)
{
if (PriceChanged != null) PriceChanged(this, e);
}
public decimal Price
{
get { return price; }
set
{
if (price == value) return;
decimal oldPrice = price;
price = value;
// 如果调用列表不为空,则触发。
if (PriceChanged != null)
OnPriceChanged(new PriceChangedEventArgs(oldPrice, price));
}
}
}
class StandaryStyle
{
static void Main(string[] args)
{
IPhone6 iphone6 = new IPhone6() { Price = 5288M };
//订阅事件 PriceChanged为事件,iphone6_PriceChanged是事件处理器
iphone6.PriceChanged += iphone6_PriceChanged;
// 调整价格(事件发生)
iphone6.Price = ;
Console.ReadKey();
}
//事件处理器 console是事件响应者
static void iphone6_PriceChanged(object sender, PriceChangedEventArgs e)
{
Console.WriteLine("年终大促销,iPhone 6 只卖 " + e.NewPrice + " 元, 原价 " + e.OldPrice + " 元,快来抢!");
}
}

运行结果:

案例中

static void iphone6_PriceChanged(object sender, PriceChangedEventArgs e)

sender参数只是传递了指向引发事件的那个类的实例的一个引用(也就是event source)

PriceChangedEventArgs e 这个很好理解,就是我们上面定义的那个PriceChangedEventArgs 类。

  为事件定义委托,它的名称通用约定以EventHandler结尾。

  由于考虑到每个事件都要定义自己的委托很麻烦,.NET 框架为我们预定义好一个通用委托System.EventHandler<TEventArgs>:(也就是前文说的语法糖)

public event EventHandler<PriceChangedEventArgs> PriceChanged;

如果不需要参数,可以直接使用EventHandler(不需要<TEventArgs>)。

最后,事件标准模式还需要写一个受保护的虚方法来触发事件,这个方法必须以On为前缀,加上事件名(PriceChanged),还要接受一个EventArgs参数,如下:

public class IPhone6 {

...

public event EventHandler<PriceChangedEventArgs> PriceChanged;

protected virtual void OnPriceChanged(PriceChangedEventArgs e) {

if (PriceChanged != null) PriceChanged(this, e);

}

...

}

三、总结

直接用一个看到的教程的图片来总结啦。本博就是这么懒,包括上面的案例也是参照另外一个教程上面的,哈哈。

C#进阶之路(二):事件的更多相关文章

  1. Spring Cloud进阶之路 | 二:服务提供者(discovery)

    1 创建父项目 以前文所述,以spring boot 2.1.7.RELEASE为基,spring cloud版本为Greenwich.SR2,spring cloud alibaba版本为2.1.0 ...

  2. linux进阶之路(二):linux文件目录

    Linux的目录结构: Linux文件系统采用级层式的目录结构,最上层是根目录"/",在此目录下再创建其他目录. 树状的文件目录.再Linux世界,一切皆文件. /etc 所有系统 ...

  3. 【SSH进阶之路】Hibernate搭建开发环境+简单实例(二)

    Hibernate是很典型的持久层框架,持久化的思想是很值得我们学习和研究的.这篇博文,我们主要以实例的形式学习Hibernate,不深究Hibernate的思想和原理,否则,一味追求,苦学思想和原理 ...

  4. 【SSH进阶之路】Spring的IOC逐层深入——为什么要使用IOC[实例讲解](二)

    上篇博客[SSH进阶之路]Spring简介,搭建Spring环境——轻量级容器框架(一),我们简单的介绍了Spring的基本概念,并且搭建了两个版本的Spring开发环境,但是我们剩下了Spring最 ...

  5. 2017PHP程序员的进阶之路

    2017PHP程序员的进阶之路 又是一年毕业季,可能会有好多毕业生即将进入开发这个圈子,踏上码农这个不归路.根据这些年在开发圈子总结的LNMP程序猿发展轨迹,结合个人经验体会,总结出很多程序员对未来的 ...

  6. [总]Android高级进阶之路

    个人Android高级进阶之路,目前按照这个目录执行,执行完毕再做扩展!!!!! 一.View的绘制 1)setContentView()的源码分析 2)SnackBar的源码分析 3)利用decor ...

  7. 【转】2017PHP程序员的进阶之路

    2017PHP程序员的进阶之路 又是一年毕业季,可能会有好多毕业生即将进入开发这个圈子,踏上码农这个不归路.根据这些年在开发圈子总结的LNMP程序猿发展轨迹,结合个人经验体会,总结出很多程序员对未来的 ...

  8. Android程序员的进阶之路

    本文主要论述的是Android程序员的进阶之路,博主本人就是一名android开发攻城狮,所以这里讲述的大多数是android开发攻城狮的技术进阶之路,如有问题请多指正. 大家都知道程序员之中有有菜鸟 ...

  9. 进阶之路 | 奇妙的Window之旅

    前言 本文已经收录到我的Github个人博客,欢迎大佬们光临寒舍: 我的GIthub博客 学习清单: Window&WindowManagerService Window&Window ...

  10. 进阶之路 | 奇妙的View之旅

    前言 本文已经收录到我的Github个人博客,欢迎大佬们光临寒舍: 我的GIthub博客 学习清单: View是什么 View的位置参数 View的触控 View的滑动 涉及以下各个知识点: View ...

随机推荐

  1. 02 Spring框架 简单配置和三种bean的创建方式

    整理了一下之前学习Spring框架时候的一点笔记.如有错误欢迎指正,不喜勿喷. 上一节学习了如何搭建SpringIOC的环境,下一步我们就来讨论一下如何利用ioc来管理对象和维护对象关系. <? ...

  2. extern "C" 有关问题

    之前帮老板编译一个库的代码,遇到了一些问题,后来发现问题出现在extern "C"语法上. 1. C/C++语法extern 关键字 extern是C/C++语言中表明函数和全局变 ...

  3. yii2判断数据库字段is null

    $query = new Query; $query->select('ID, City,State,StudentName') ->from('student') ->]) -&g ...

  4. facebook开源了他们的分布式大数据DB

    https://github.com/facebook/presto facebook 3天前开源了他们的 分布式大数据DB Distributed SQL query engine for big ...

  5. codeforces上某题

    一道codeforces上的题目. 题目大意: 定义有k个不同的字符的字符串为好字符串.现在给出一个字符串,求解对该字符串的每个前缀Si至少是多少个好字符串的连接,若不能由好字符串连接而成则输出-1. ...

  6. 20165101刘天野 2017-2018-2 《Java程序设计》第6周学习总结

    #20165101刘天野 2017-2018-2 <Java程序设计>第6周学习总结 教材学习内容总结 第八章:常用实用类 String类:不可变类,一些看起来能够改变String的方法其 ...

  7. iOS下的WiFi开发

    iOS下Wi-Fi开发需要添加依赖库SystemConfiguration.framework,在需要使用Wi-Fi信息的控制器下引入头文件#import <SystemConfiguratio ...

  8. SpringBoot Mybatis keyProperty和useGeneratedKeys的作用

    在使用mybatis时,常常会出现这种需求: 当主键是自增的情况下,添加一条记录的同时,其主键是不能被使用的(为空),但是有时我们需要该主键,这时我们该如何处理呢?这时我们只需要在其对应xml中加入以 ...

  9. sql server update时,是行锁还是表锁

    https://bbs.csdn.net/topics/120000749 http://www.cnblogs.com/s021368/articles/2148659.html 问题: udpat ...

  10. 【转载】Java类加载原理解析

    Java类加载原理解析 原文出处:http://www.blogjava.net/zhuxing/archive/2008/08/08/220841.html 1       基本信息 摘要: 每个j ...