[空白_音频] Hi
同学你好，下面我们讲 C# 当中的事件
大体上来说呢，C# 当中的事件呢就相当于回调函数
我们知道呢，我们当发生一件事情，我们经常要调另一个函数
这个呢函数呢我们就称为回调函数 Callback
其实事件呢我们在前面已经用到了，比如说在用户界面当中的事件
当按钮点击的时候，按钮点击的时候呢我们写了一个 Click
+= this.
this.button1.Click +=,
加等呢 加等于一个事件处理器，System.EventHandler 然后这里面呢封装的是一个函数。
也就是说 它相当于，当这个 Click 事件发生的时候，我们就会去调用 button1
Click 这个函数 从这个意义上来说，事件呢我们叫注册了一个事件
它实际上就是当它某一个情况下，它才去调这个函数。
当这个事件发生的时候呢 它还会带一些参数过来，比如说是谁发生的这个事件，叫
sender 以及呢事件的参数 EventArgs
就是，那它的基本写法就是一个 +=
然后呢加等于一个函数，但这个函数很显然，我们要包装成一个 这里是一个 delegate。
我们用 别人的事件呢，系统定义好的事件呢就比较方便。
如果我自己要 声明一个自己的事件，那么基本上是需要这样的，第一个呢我声明一个事件
这个事件呢它也是一个要声明的
那么在某一个类里头声明一个事件，用一个关键词叫 event event。
一般说来事件呢当然都是公开的，public public event。
然后委托的名字，因为 事件呢我们刚才讲了，它本质上是要调某个函数 所以它一定有个类型，这个类型呢就是函数原型。
或者在 C# 里面我们就叫做 delegate 就是委托的类型。
然后这个是事件名，相当于一个变量 就是什么样的类型的，一个什么样的名字的事件
那这是首先我们要声明一个事件。
在别的地方呢我们就要注册，就是 就是加等和减等，实际上就是注册这个函数或者移去这个函数
要注意的是在事件所在的类的外面
就是在别的类里头，我们要用这个事件，只能用加等或减等，不能用其他的
比如说等于啊等等，它不能用别的运算符 只能注册或者取消注册这个事件
而在这个事件里头我们要激发这个事件，就是要调用 这个回调函数。
那它就是事件的这个名字，因为它本身就是一个 变量名了，然后要带一些参数，实际上就是调用别人的函数
那相当于回调所注册的函数 这样说起呢有点抽象，我们下面看个例子。
比如说我们要写 一个网络爬虫的程序。
大家知道网络爬虫的 程序呢它要做比较复杂的工程，它要去下载，然后呢 下载的过程呢也比较慢。
那我们希望呢，在这个类里面 它能够告诉我们下载了多少，或者呢这个已经
某个下载已经完成了，就是，或者某个下载刚刚开始。
所以它要告诉我们这些 事件发生了以后它要调我们的函数的话 我在里面呢就要声明这个事件。
那我们先看看这个调用过程 这是我写的网络爬虫里面的一个爬行函数。
这个爬行函数呢 它为了在，为了在这个刚刚开始
刚刚开始调用爬行的过程呢，我们就 DownloadStart 就是这个事件发生了，这个事件发生了。
它实际上就是告诉别人说 就相当于我们说按钮点击了，好，它就调用 别的这个函数。
但是呢，因为别的函数呢，它本身呢是 一个变量，是一个别人注册的事件。
所以在这里看呢，那就是调这个。
同样的呢 当它正在下载百分之多少，我们也会
通知，通知调用者，告诉他说，正在下载，然后下载百分之多少，我们也 告诉他。
类似的，下载结束也告诉他。
所以为了达到这个效果呢我们要做几件事情 哪几件事情呢？可以说是六件事情，六步曲
这里呢看六步曲呢，看起来呢有点点啰嗦，有点复杂
然后我们把它再分解一下，就是有三步。
第一步呢，我要声明一个，因为 声明一个类型。
因为我们知道事件呢它就相当于回调别人一个函数 所以呢我们就要声明一个委托。
这个委托呢 按照习惯，就是 delegate 一般是 void
返回，然后叫什么什么 EventHandler 经常呢带两个类型的参数，一个是
object，相当于呢事件源，是谁发出了事件 第二个呢，事件的详情，也有另外一个参数。
所以这个详情呢 为了表示更明确呢，包括比如说我们刚才说的下载了百分之多少啊等等
要把那些参数呢给包装进去，所以我们又要定一个类。
所以 第一步呢就是，这个声明一个委托，是为我们后面呢起作用的 那当然声明委托的时候呢我们经常又要附带做一件事情呢
就是这个参数，具体发生这个事件的情形 好这是一个，声明一个委托。
第二个事情呢，就是 我们要定义这个事件本身，那就是在某个类，比如说刚才是网络爬虫里面我们声明一个
public event，然后是什么样的类型，什么样的名称
这是定义事件，相当于定义了一个成员，相当于定义了成员。
那么在函数里头 比如说下载在某种情况下，然后去激发这个事件 那就是事件名，然后一个参数。
这个参数呢根据情况来形成这个参数
那这样一步呢就是在这个类里面把这个事件给定义好了，一个是要
定义一个成员叫 event 这个类型。
然后发生 那个事件呢就是调这个事件，然后带一些参数去调用它
然后在别的类里面，就是在别的地方我们怎么去 用这个事件呢？那我们首先要
定义一个回调的这个方法，具体这个方法。
那这个方法呢要跟我们刚才声明的这个方法呢 类型相同。
注册一个事件呢就是加等，然后取消 注册呢就是减等，加等。
那这里呢都是一个 new，一个委托 我们再回到刚才那个例子呢，再浏览一下
就我们这里呢声明了 三个
delegate，一个是呢叫做 DownloadStart
开始的 Handler，然后呢 DownloadEnd 结束的 Handler 一个是 Downloading，就是正在下载的 Handler
它的参数呢一个是 object sender，另一个参数呢我们
定义呢开始的一个参数，表示 开始信息。
这个里面呢包括它的 Url，正在下载的
然后结束呢，我们可以呢，一共下载了多少字节 加载了多少字节。
同样的，我们也可以定义一个参数呢是 正在下载，里面呢一个是
Url，就是正在下载的网址，一个 Percent 百分之多少。
然后 真正定义这个网络爬虫函数里面我们就声明了这三个事件
public event 什么什么 Handler，然后呢这是变量名
就相当于我们说的 Click 点击，这里就是 Start，End
和 Downloading 而激发这个事件呢就是在下载的过程当中
这个函数里头，我们就调用这个事件，也就是调用这个 delegate
这是这样一个过程 那别人怎么用呢？那就是我们利用一个
Crawler，就是这个爬行程序 然后，爬行程序点那个
DownloadStart 加等于一个函数，加等于一个函数。
当然这个函数呢 封装成一个 delegate。
就相当于我们平时写的 button1.
Click 加等于一个函数，一样的道理 所以自定义函数呢，看起来有点复杂，但实际上
就是这样，我们先声明一个委托，以及附带的参数
而在这个类里头呢，就是声明一个成员叫做 event 成员。
而发生事件呢就是调用它，在别的地方呢就是加等于它 所以六步实际上是三大步
这里呢再 谈一下事件与委托的关系。
事件呢，它的声明呢 是一个成员，所以有点像委托的一个实例，或者说委托，某种委托类型的变量
因为事件呢它就是要有相关的委托类型
同时呢，它跟普通的委托一样，它也包含了多个函数 但事件稍微有点不同的就是事件的运算呢
在这个类定义这个事件的别的类，也就是说要调用者呢只能用加等或减等，不能用其他的
还有一个，事件与委托相比
事件比普通的委托实例还要复杂，这是为什么呢？
事件，我们在定义的时候可以定义一个事件的存取器，这个存取器呢 它不像我们属性的存取器一个
set, get，这个 事件名后面可以写一对花括号，这里面可以定义一个 add 和 remove add 和 remove。
它默认的就相当于 add 就是加等于一个 这个委托，那 remove
呢就是减等一个委托，也就是说减等了函数 在一定意义上，我们平时
不写 add 和 remove，它相当于呢，是对
这个实例变量的一个自动定义了 add 和 remove
很显然，它就比普通的一个单独的一个变量 要复杂。
总的说来呢 事件呢它是一种消息机制，它可以通知别人 那这个通知的办法呢就是让别人
来注册我这个事件，我在这里头呢去调用它 它是一个回调，也就是说，它不是上来就调用
它是当某种情况下才去调用别人的函数 所以它是一种回调函数。
当然事件的类型呢 它总是一个委托，也就是说是某种函数原型
事件是 C# 里面呢最特别的一个语法成分
在很多语言里面呢它是没有事件，当然它用别的方式来实践，那很显然就复杂得多 所以事件是
C# 里面，我觉得是最有特色的 部分，也是我们用的很多的部分。
它把 发出事件者和调用事件者呢给隔离开了，这样我们就带来更大的灵活性
事件确实是太好的一个语法