1
00:00:00,000 --> 00:00:05,689
[空白_音频] Hi

2
00:00:05,689 --> 00:00:10,390
同学你好，下面我们讲 C# 当中的事件

3
00:00:10,390 --> 00:00:16,820
大体上来说呢，C# 当中的事件呢就相当于回调函数

4
00:00:16,820 --> 00:00:24,010
我们知道呢，我们当发生一件事情，我们经常要调另一个函数

5
00:00:24,010 --> 00:00:29,640
这个呢函数呢我们就称为回调函数 Callback

6
00:00:29,640 --> 00:00:34,850
其实事件呢我们在前面已经用到了，比如说在用户界面当中的事件

7
00:00:34,850 --> 00:00:41,750
当按钮点击的时候，按钮点击的时候呢我们写了一个 Click

8
00:00:41,750 --> 00:00:46,155
+= this.

9
00:00:46,155 --> 00:00:50,118
this.button1.Click +=,

10
00:00:50,118 --> 00:00:59,034
加等呢 加等于一个事件处理器，System.EventHandler
然后这里面呢封装的是一个函数。

11
00:00:59,034 --> 00:01:05,664
也就是说 它相当于，当这个 Click
事件发生的时候，我们就会去调用 button1

12
00:01:05,664 --> 00:01:12,280
Click 这个函数 从这个意义上来说，事件呢我们叫注册了一个事件

13
00:01:12,280 --> 00:01:17,571
它实际上就是当它某一个情况下，它才去调这个函数。

14
00:01:17,571 --> 00:01:25,946
当这个事件发生的时候呢 它还会带一些参数过来，比如说是谁发生的这个事件，叫

15
00:01:25,946 --> 00:01:31,690
sender 以及呢事件的参数 EventArgs

16
00:01:31,690 --> 00:01:37,170
就是，那它的基本写法就是一个 +=

17
00:01:37,170 --> 00:01:46,508
然后呢加等于一个函数，但这个函数很显然，我们要包装成一个 这里是一个
delegate。

18
00:01:46,508 --> 00:01:50,776
我们用 别人的事件呢，系统定义好的事件呢就比较方便。

19
00:01:50,776 --> 00:01:58,700
如果我自己要 声明一个自己的事件，那么基本上是需要这样的，第一个呢我声明一个事件

20
00:01:58,700 --> 00:02:03,455
这个事件呢它也是一个要声明的

21
00:02:03,455 --> 00:02:09,223
那么在某一个类里头声明一个事件，用一个关键词叫 event event。

22
00:02:09,223 --> 00:02:14,795
一般说来事件呢当然都是公开的，public public event。

23
00:02:14,795 --> 00:02:24,643
然后委托的名字，因为 事件呢我们刚才讲了，它本质上是要调某个函数
所以它一定有个类型，这个类型呢就是函数原型。

24
00:02:24,643 --> 00:02:29,557
或者在 C# 里面我们就叫做 delegate 就是委托的类型。

25
00:02:29,557 --> 00:02:39,440
然后这个是事件名，相当于一个变量 就是什么样的类型的，一个什么样的名字的事件

26
00:02:39,440 --> 00:02:42,634
那这是首先我们要声明一个事件。

27
00:02:42,634 --> 00:02:52,410
在别的地方呢我们就要注册，就是
就是加等和减等，实际上就是注册这个函数或者移去这个函数

28
00:02:52,410 --> 00:02:56,800
要注意的是在事件所在的类的外面

29
00:02:56,800 --> 00:03:04,430
就是在别的类里头，我们要用这个事件，只能用加等或减等，不能用其他的

30
00:03:04,430 --> 00:03:11,150
比如说等于啊等等，它不能用别的运算符 只能注册或者取消注册这个事件

31
00:03:11,150 --> 00:03:17,033
而在这个事件里头我们要激发这个事件，就是要调用 这个回调函数。

32
00:03:17,033 --> 00:03:26,650
那它就是事件的这个名字，因为它本身就是一个
变量名了，然后要带一些参数，实际上就是调用别人的函数

33
00:03:26,650 --> 00:03:35,179
那相当于回调所注册的函数 这样说起呢有点抽象，我们下面看个例子。

34
00:03:35,179 --> 00:03:38,867
比如说我们要写 一个网络爬虫的程序。

35
00:03:38,867 --> 00:03:48,058
大家知道网络爬虫的 程序呢它要做比较复杂的工程，它要去下载，然后呢
下载的过程呢也比较慢。

36
00:03:48,058 --> 00:03:56,220
那我们希望呢，在这个类里面 它能够告诉我们下载了多少，或者呢这个已经

37
00:03:56,220 --> 00:04:00,962
某个下载已经完成了，就是，或者某个下载刚刚开始。

38
00:04:00,962 --> 00:04:09,264
所以它要告诉我们这些 事件发生了以后它要调我们的函数的话
我在里面呢就要声明这个事件。

39
00:04:09,264 --> 00:04:16,703
那我们先看看这个调用过程 这是我写的网络爬虫里面的一个爬行函数。

40
00:04:16,703 --> 00:04:23,930
这个爬行函数呢 它为了在，为了在这个刚刚开始

41
00:04:23,930 --> 00:04:32,796
刚刚开始调用爬行的过程呢，我们就 DownloadStart
就是这个事件发生了，这个事件发生了。

42
00:04:32,796 --> 00:04:40,157
它实际上就是告诉别人说 就相当于我们说按钮点击了，好，它就调用 别的这个函数。

43
00:04:40,157 --> 00:04:47,073
但是呢，因为别的函数呢，它本身呢是 一个变量，是一个别人注册的事件。

44
00:04:47,073 --> 00:04:49,659
所以在这里看呢，那就是调这个。

45
00:04:49,659 --> 00:04:53,400
同样的呢 当它正在下载百分之多少，我们也会

46
00:04:53,400 --> 00:05:01,251
通知，通知调用者，告诉他说，正在下载，然后下载百分之多少，我们也 告诉他。

47
00:05:01,251 --> 00:05:03,927
类似的，下载结束也告诉他。

48
00:05:03,927 --> 00:05:13,770
所以为了达到这个效果呢我们要做几件事情 哪几件事情呢？可以说是六件事情，六步曲

49
00:05:13,770 --> 00:05:21,110
这里呢看六步曲呢，看起来呢有点点啰嗦，有点复杂

50
00:05:21,110 --> 00:05:24,443
然后我们把它再分解一下，就是有三步。

51
00:05:24,443 --> 00:05:28,662
第一步呢，我要声明一个，因为 声明一个类型。

52
00:05:28,662 --> 00:05:36,687
因为我们知道事件呢它就相当于回调别人一个函数 所以呢我们就要声明一个委托。

53
00:05:36,687 --> 00:05:41,424
这个委托呢 按照习惯，就是 delegate 一般是 void

54
00:05:41,424 --> 00:05:47,678
返回，然后叫什么什么 EventHandler 经常呢带两个类型的参数，一个是

55
00:05:47,678 --> 00:05:55,605
object，相当于呢事件源，是谁发出了事件
第二个呢，事件的详情，也有另外一个参数。

56
00:05:55,605 --> 00:06:01,680
所以这个详情呢 为了表示更明确呢，包括比如说我们刚才说的下载了百分之多少啊等等

57
00:06:01,680 --> 00:06:06,197
要把那些参数呢给包装进去，所以我们又要定一个类。

58
00:06:06,197 --> 00:06:15,900
所以 第一步呢就是，这个声明一个委托，是为我们后面呢起作用的
那当然声明委托的时候呢我们经常又要附带做一件事情呢

59
00:06:15,900 --> 00:06:24,125
就是这个参数，具体发生这个事件的情形 好这是一个，声明一个委托。

60
00:06:24,125 --> 00:06:32,840
第二个事情呢，就是
我们要定义这个事件本身，那就是在某个类，比如说刚才是网络爬虫里面我们声明一个

61
00:06:32,840 --> 00:06:37,170
public event，然后是什么样的类型，什么样的名称

62
00:06:37,170 --> 00:06:43,047
这是定义事件，相当于定义了一个成员，相当于定义了成员。

63
00:06:43,047 --> 00:06:52,279
那么在函数里头 比如说下载在某种情况下，然后去激发这个事件
那就是事件名，然后一个参数。

64
00:06:52,279 --> 00:06:55,270
这个参数呢根据情况来形成这个参数

65
00:06:55,270 --> 00:07:02,750
那这样一步呢就是在这个类里面把这个事件给定义好了，一个是要

66
00:07:02,750 --> 00:07:06,262
定义一个成员叫 event 这个类型。

67
00:07:06,262 --> 00:07:11,340
然后发生 那个事件呢就是调这个事件，然后带一些参数去调用它

68
00:07:11,340 --> 00:07:20,610
然后在别的类里面，就是在别的地方我们怎么去 用这个事件呢？那我们首先要

69
00:07:20,610 --> 00:07:24,445
定义一个回调的这个方法，具体这个方法。

70
00:07:24,445 --> 00:07:29,496
那这个方法呢要跟我们刚才声明的这个方法呢 类型相同。

71
00:07:29,496 --> 00:07:35,775
注册一个事件呢就是加等，然后取消 注册呢就是减等，加等。

72
00:07:35,775 --> 00:07:44,000
那这里呢都是一个 new，一个委托 我们再回到刚才那个例子呢，再浏览一下

73
00:07:44,000 --> 00:07:48,927
就我们这里呢声明了 三个

74
00:07:48,927 --> 00:07:54,488
delegate，一个是呢叫做 DownloadStart

75
00:07:54,488 --> 00:08:02,400
开始的 Handler，然后呢 DownloadEnd 结束的 Handler
一个是 Downloading，就是正在下载的 Handler

76
00:08:02,400 --> 00:08:07,240
它的参数呢一个是 object sender，另一个参数呢我们

77
00:08:07,240 --> 00:08:12,404
定义呢开始的一个参数，表示 开始信息。

78
00:08:12,404 --> 00:08:15,570
这个里面呢包括它的 Url，正在下载的

79
00:08:15,570 --> 00:08:22,554
然后结束呢，我们可以呢，一共下载了多少字节 加载了多少字节。

80
00:08:22,554 --> 00:08:27,802
同样的，我们也可以定义一个参数呢是 正在下载，里面呢一个是

81
00:08:27,802 --> 00:08:33,760
Url，就是正在下载的网址，一个 Percent 百分之多少。

82
00:08:33,760 --> 00:08:39,141
然后 真正定义这个网络爬虫函数里面我们就声明了这三个事件

83
00:08:39,141 --> 00:08:44,680
public event 什么什么 Handler，然后呢这是变量名

84
00:08:44,680 --> 00:08:49,733
就相当于我们说的 Click 点击，这里就是 Start，End

85
00:08:49,733 --> 00:08:57,320
和 Downloading 而激发这个事件呢就是在下载的过程当中

86
00:08:57,320 --> 00:09:03,260
这个函数里头，我们就调用这个事件，也就是调用这个 delegate

87
00:09:03,260 --> 00:09:12,095
这是这样一个过程 那别人怎么用呢？那就是我们利用一个

88
00:09:12,095 --> 00:09:19,440
Crawler，就是这个爬行程序 然后，爬行程序点那个

89
00:09:19,440 --> 00:09:23,338
DownloadStart 加等于一个函数，加等于一个函数。

90
00:09:23,338 --> 00:09:26,389
当然这个函数呢 封装成一个 delegate。

91
00:09:26,389 --> 00:09:29,281
就相当于我们平时写的 button1.

92
00:09:29,281 --> 00:09:38,000
Click 加等于一个函数，一样的道理 所以自定义函数呢，看起来有点复杂，但实际上

93
00:09:38,000 --> 00:09:42,240
就是这样，我们先声明一个委托，以及附带的参数

94
00:09:42,240 --> 00:09:48,098
而在这个类里头呢，就是声明一个成员叫做 event 成员。

95
00:09:48,098 --> 00:09:57,500
而发生事件呢就是调用它，在别的地方呢就是加等于它 所以六步实际上是三大步

96
00:09:57,500 --> 00:10:05,323
这里呢再 谈一下事件与委托的关系。

97
00:10:05,323 --> 00:10:15,160
事件呢，它的声明呢
是一个成员，所以有点像委托的一个实例，或者说委托，某种委托类型的变量

98
00:10:15,160 --> 00:10:19,340
因为事件呢它就是要有相关的委托类型

99
00:10:19,340 --> 00:10:28,740
同时呢，它跟普通的委托一样，它也包含了多个函数 但事件稍微有点不同的就是事件的运算呢

100
00:10:28,740 --> 00:10:36,550
在这个类定义这个事件的别的类，也就是说要调用者呢只能用加等或减等，不能用其他的

101
00:10:36,550 --> 00:10:42,980
还有一个，事件与委托相比

102
00:10:42,980 --> 00:10:47,440
事件比普通的委托实例还要复杂，这是为什么呢？

103
00:10:47,440 --> 00:10:57,281
事件，我们在定义的时候可以定义一个事件的存取器，这个存取器呢
它不像我们属性的存取器一个

104
00:10:57,281 --> 00:11:07,198
set, get，这个 事件名后面可以写一对花括号，这里面可以定义一个
add 和 remove add 和 remove。

105
00:11:07,198 --> 00:11:12,152
它默认的就相当于 add 就是加等于一个 这个委托，那 remove

106
00:11:12,152 --> 00:11:19,500
呢就是减等一个委托，也就是说减等了函数 在一定意义上，我们平时

107
00:11:19,500 --> 00:11:24,030
不写 add 和 remove，它相当于呢，是对

108
00:11:24,030 --> 00:11:28,560
这个实例变量的一个自动定义了 add 和 remove

109
00:11:28,560 --> 00:11:36,085
很显然，它就比普通的一个单独的一个变量 要复杂。

110
00:11:36,085 --> 00:11:45,420
总的说来呢 事件呢它是一种消息机制，它可以通知别人 那这个通知的办法呢就是让别人

111
00:11:45,420 --> 00:11:54,870
来注册我这个事件，我在这里头呢去调用它 它是一个回调，也就是说，它不是上来就调用

112
00:11:54,870 --> 00:12:01,392
它是当某种情况下才去调用别人的函数 所以它是一种回调函数。

113
00:12:01,392 --> 00:12:07,840
当然事件的类型呢 它总是一个委托，也就是说是某种函数原型

114
00:12:07,840 --> 00:12:14,130
事件是 C# 里面呢最特别的一个语法成分

115
00:12:14,130 --> 00:12:22,375
在很多语言里面呢它是没有事件，当然它用别的方式来实践，那很显然就复杂得多 所以事件是

116
00:12:22,375 --> 00:12:30,378
C# 里面，我觉得是最有特色的 部分，也是我们用的很多的部分。

117
00:12:30,378 --> 00:12:38,330
它把 发出事件者和调用事件者呢给隔离开了，这样我们就带来更大的灵活性

118
00:12:38,330 --> 00:12:43,240
事件确实是太好的一个语法
