目录
l
导论
l
什么是委托
l
事件的理解
l
事件
关键字
l
最后
导论
在学习
C#
中的委托和事件过程中,我读了许多文章来理解他们二者究竟是怎么一回事,以及如何使用他们,现在我将整个的理解过程陈述以下,我学到的每一方面,恐怕也是你们需要掌握的
:
-
)。
什么是委托?
委托和事件这两个概念是完全配合的。委托仅仅是函数指针,那就是说,它能够引用函数,通过传递地址的机制完成。委托是一个类,当你对它实例化时,要提供一个引用函数,将其作为它构造函数的参数。
每一个委托都有自己的签名,例如:
Delegate int SomeDelegate(string s, bool b);
是一个委托申明,在这里,提及的签名,就是说
SomeDelegate
这个委托
有
string
和
bool
类型的形参,返回一个
int
类型。
上面提及的:当你对委托实例化时,要提供一个引用函数,将其作为它构造函数的参数。这里要注意了:被引用的这个函数必须和委托有相同的签名。
看下面的函数:
private int SomeFunction(string str, bool bln){...}
你可以把这个函数传给
SomeDelegate
的构造函数,因为他们有相似的签名(
in other words
,他们都有相同的形参类型和个数,并且返回相同的数据类型)。
SomeDelegate sd = new SomeDelegate(SomeFunction);
sd
引用了
SomeFunction
,也就是说,
SomeFunction
已被
sd
所登记注册,如果你调用
sd
,
SomeFunction
这个函数也会被调用,记住:我所说
SomeFunction
的含义,后面,我们会用到它。
现在,你应该知道如何使用委托了,让我们继续理解事件之旅
……
事件的理解
我们知道,在
C#
中:
l
按钮(
Button
)就是一个类,当我们单击它时,就触发一次
click
事件。
l
时钟(
Timer
)也是一个类,每过一毫秒,就触发一次
tick
事件。
让我们通过一个例子来学习,假定有这样的情节:
现在有一个
Counter
的类,它有一个方法
CountTo(int countTo, int reachableNum)
,该方法表示:在指定的时间段内(
0~~countTo
),当到达指定的时间点
reachableNum
时,就触发一次
NumberReached
事件。
它还有一个事件:
NumberReached
,事件是委托类型的变量。意思是:如果给事件命名,用
event
关键字和要使用的委托类型申明它即可,如下所示:
public event NumberReachedEventHandler NumberReached;
在上面的申明中,
NumberReachedEventHandle
仅是一个委托,更确切的表示应该是:
NumberReachedDelegate
。但是微软从不这样认为
MouseDelegate
或者
PaintDelegate
,,而是称谓:
MouseEventHandler
或者
PaintEventHandler
。所以
NumberReachedEventHandler
比
NumberReachedDelegate
听起来更方便一些,
OK?
好了,让我们继续,现在你知道了,在我们声明事件之前,需要象下面这样的形式来定义委托:
public delegate void NumberReachedEventHandler(object sender, NumberReachedEventArgs e);
现在声明的委托
NumberReachedEventHandle
,它有一个
void
返回值,和
object
,
NumberReachedEventArgs
两个形参。就像我们在第一节中强调的那样,当实例化委托时,作为实参传入的函数也必须拥有和委托同样的签名。
在你的代码中,
你是否用过
PaintEventArgs
或者
MouseEventArgs
来确定鼠标的移动位置?是否在触发
Paint
事件的对象中用过
Graphics
属性?实际上,为用户提供数据的类都是继承于
System.EventArgs
类,就是我们常说的事件参数类,如果事件不提供参数,就不定义该类。在我们的例子中,我们通过下面的类提供预期的时间点。
public class NumberReachedEventArgs : EventArgs
{
private int _reached;
public NumberReachedEventArgs(int num)
{
this._reached = num;
}
public int ReachedNumber
{
get
{
return _reached;
}
}
}
好,有了前面的介绍,让我们到
Counter
类里面看看:
namespace Events
{
public delegate void NumberReachedEventHandler(object sender,
NumberReachedEventArgs e);
/// <summary>
/// Summary description for Counter.
/// </summary>
public class Counter
{
public event NumberReachedEventHandler NumberReached;
public Counter()
{
//
// TODO: Add constructor logic here
//
}
public void CountTo(int countTo, int reachableNum)
{
if(countTo < reachableNum)
throw new ArgumentException(
"reachableNum should be less than countTo");
for(int ctr=0;ctr<=countTo;ctr++)
{
if(ctr == reachableNum)
{
NumberReachedEventArgs e = new NumberReachedEventArgs(
reachableNum);
OnNumberReached(e);
return;//don't count any more
}
}
}
protected virtual void OnNumberReached(NumberReachedEventArgs e)
{
if(NumberReached != null)
{
NumberReached(this, e);//Raise the event
}
}
}
在
Counter
中,如果到达指定的时间点,就触发一次事件,有以下几个方面需要注意:
l
通过调用
NumberReached
(它是
NumberReachedEventHandler
委托的实例)来完成一次触发事件。
NumberReached(this, e);
通过这种方式,可以调用所有的注册函数。
l
通过
NumberReachedEventArgs e = new NumberReachedEventArgs(reachableNum);
为所有的注册函数提供事件数据。
l
看了上面的代码,你可能要问了:为什么我们直接用
OnNumberReached(NumberReachedEventArgs e)
方法来调用
NumberReached
(
this
,
e
),而不用下面的代码呢?
if(ctr == reachableNum)
{
NumberReachedEventArgs e = new NumberReachedEventArgs(reachableNum);
//OnNumberReached(e);
if(NumberReached != null)
{
NumberReached(this, e);//Raise the event
}
return;//don't count any more
}
这个问题问得很好,那就让我们再看一下
OnNumberReached
签名:
protected virtual void OnNumberReached(NumberReachedEventArgs e)
①
你也明白
关键字
protected
限定了
只有从该类继承的类才能调用该类中的所有方法。
②
关键字
virtual
表明了
在继承类中可以重写该方法。
这两点非常有用,假设你在写一个从
Counter
继承而来的类,通过重写
OnNumberReached
方法,你可以在事件触发之前,进行一次其他的工作。
protected override void OnNumberReached(NumberReachedEventArgs e)
{
//Do additional work
base.OnNumberReached(e);
}
注意:如果你没有调用
base.OnNumberReached(e),
那么从不会触发这个事件!在你继承该类而想剔出它的一些其他事件时,使用该方式是非常有用的。
l
还要注意到:委托
NumberReachedEventHandler
是在类定义的外部,命名空间内定义的,对所有类来说是可见的。
好,该我们来实际操作使用
Counter
类了。
在我们简单的应用程序中,我们有两个文本框,分别是:
txtCountTo
和
txtReachable
:

下面是 btnRun 的 click 事件:
private
void btnRun_Click(object sender, System.EventArgs e)
{
if(txtCountTo.Text == "" || txtReachable.Text=="")
return;
oCounter.CountTo(Convert.ToInt32(txtCountTo.Text), Convert.ToInt32(txtReachable.Text));
}
private
void oCounter_NumberReached(object sender, NumberReachedEventArgs e)
{
MessageBox.Show("Reached: " + e.ReachedNumber.ToString());
}
初始化事件处理的语法如下:
oCounter = new Counter();
oCounter.NumberReached += new NumberReachedEventHandler(oCounter_NumberReached);
现在你明白了你刚才所做的一切,仅仅初始化 NumberReachedEventHandler 委托类型的对象(就像你实例化其他对象一样),注意到 oCounter_NumberReached 方法的签名与我前面提到的相似。
还要注意我们用的是+= 而不是=;这是因为委托是特殊的对象,它可以引用多个对象(在这里是指它可以引用多个函数)。For example 如果有另外一个
和oCounter_NumberReached一样具有相同签名的函数oCounter_NumberReached2,这两个函数都可以被引用:
oCounter = new Counter();
oCounter.NumberReached += new NumberReachedEventHandler(oCounter_NumberReached);
oCounter.NumberReached += new NumberReachedEventHandler(oCounter_NumberReached2);
现在,触发一个事件后,上面两个函数被依次调用。
视情况而定,如果你想让oCounter_NumberReached2在NumberReached事件发生后不再被调用,可以简单地这样写:oCounter.NumberReached -= new NumberReachedEventHandler(oCounter_NumberReached2);
最后
让我们看一下完整的源代码,以供参考:
Form1.cs
Counter.cs
Counter.cs
1
using
System;
2
using
System.Drawing;
3
using
System.Collections;
4
using
System.ComponentModel;
5
using
System.Windows.Forms;
6
using
System.Data;
7
8
namespace
Events
9
{
10
/**//**//**//// <summary>
11
/// Summary description for Form1.
12
/// </summary>
13
public class Form1 : System.Windows.Forms.Form
14
{
15
Counter oCounter = null;
16
17
private System.Windows.Forms.Button cmdRun;
18
private System.Windows.Forms.TextBox txtReachable;
19
private System.Windows.Forms.TextBox txtCountTo;
20
private System.Windows.Forms.Label label1;
21
private System.Windows.Forms.Label label2;
22
private System.Windows.Forms.Button btnRemoveDelegate;
23
/**//**//**//// <summary>
24
/// Required designer variable.
25
/// </summary>
26
private System.ComponentModel.Container components = null;
27
28
public Form1()
29
{
30
//
31
// Required for Windows Form Designer support
32
//
33
InitializeComponent();
34
35
//
36
// TODO: Add any constructor code after InitializeComponent call
37
//
38
oCounter = new Counter();
39
oCounter.NumberReached += new NumberReachedEventHandler(oCounter_NumberReached);
40
oCounter.NumberReached += new NumberReachedEventHandler(oCounter_NumberReached2);
41
}
42
43
/**//**//**//// <summary>
44
/// Clean up any resources being used.
45
/// </summary>
46
protected override void Dispose( bool disposing )
47
{
48
if( disposing )
49
{
50
if (components != null)
51
{
52
components.Dispose();
53
}
54
}
55
base.Dispose( disposing );
56
}
57
58
Windows Form Designer generated codeWindows Form Designer generated code#region Windows Form Designer generated code
59
/**//**//**//// <summary>
60
/// Required method for Designer support - do not modify
61
/// the contents of this method with the code editor.
62
/// </summary>
63
private void InitializeComponent()
64
{
65
this.cmdRun = new System.Windows.Forms.Button();
66
this.txtReachable = new System.Windows.Forms.TextBox();
67
this.txtCountTo = new System.Windows.Forms.TextBox();
68
this.label1 = new System.Windows.Forms.Label();
69
this.label2 = new System.Windows.Forms.Label();
70
this.btnRemoveDelegate = new System.Windows.Forms.Button();
71
this.SuspendLayout();
72
//
73
// cmdRun
74
//
75
this.cmdRun.Location = new System.Drawing.Point(16, 72);
76
this.cmdRun.Name = "cmdRun";
77
this.cmdRun.Size = new System.Drawing.Size(48, 23);
78
this.cmdRun.TabIndex = 2;
79
this.cmdRun.Text = "Run";
80
this.cmdRun.Click += new System.EventHandler(this.cmdRun_Click);
81
//
82
// txtReachable
83
//
84
this.txtReachable.Location = new System.Drawing.Point(144, 40);
85
this.txtReachable.Name = "txtReachable";
86
this.txtReachable.Size = new System.Drawing.Size(56, 20);
87
this.txtReachable.TabIndex = 1;
88
this.txtReachable.Text = "";
89
//
90
// txtCountTo
91
//
92
this.txtCountTo.Location = new System.Drawing.Point(144, 16);
93
this.txtCountTo.Name = "txtCountTo";
94
this.txtCountTo.Size = new System.Drawing.Size(56, 20);
95
this.txtCountTo.TabIndex = 0;
96
this.txtCountTo.Text = "";
97
//
98
// label1
99
//
100
this.label1.AutoSize = true;
101
this.label1.Location = new System.Drawing.Point(16, 16);
102
this.label1.Name = "label1";
103
this.label1.Size = new System.Drawing.Size(51, 13);
104
this.label1.TabIndex = 3;
105
this.label1.Text = "Count To";
106
//
107
// label2
108
//
109
this.label2.AutoSize = true;
110
this.label2.Location = new System.Drawing.Point(16, 40);
111
this.label2.Name = "label2";
112
this.label2.Size = new System.Drawing.Size(99, 13);
113
this.label2.TabIndex = 4;
114
this.label2.Text = "Reach this number";
115
//
116
// btnRemoveDelegate
117
//
118
this.btnRemoveDelegate.Location = new System.Drawing.Point(16, 104);
119
this.btnRemoveDelegate.Name = "btnRemoveDelegate";
120
this.btnRemoveDelegate.Size = new System.Drawing.Size(168, 23);
121
this.btnRemoveDelegate.TabIndex = 5;
122
this.btnRemoveDelegate.Text = "Remove second handler";
123
this.btnRemoveDelegate.Click += new System.EventHandler(this.btnRemoveDelegate_Click);
124
//
125
// Form1
126
//
127
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
128
this.ClientSize = new System.Drawing.Size(224, 134);
129
this.Controls.AddRange(new System.Windows.Forms.Control[]
{
130
this.btnRemoveDelegate,
131
this.label2,
132
this.label1,
133
this.txtCountTo,
134
this.txtReachable,
135
this.cmdRun});
136
this.Name = "Form1";
137
this.Text = "Events";
138
this.ResumeLayout(false);
139
140
}
141
#endregion
142
143
/**//**//**//// <summary>
144
/// The main entry point for the application.
145
/// </summary>
146
[STAThread]
147
static void Main()
148
{
149
Application.Run(new Form1());
150
}
151
152
private void btnRun_Click(object sender, System.EventArgs e)
153
{
154
if(txtCountTo.Text == "" || txtReachable.Text=="")
155
return;
156
oCounter.CountTo(Convert.ToInt32(txtCountTo.Text), Convert.ToInt32(txtReachable.Text));
157
}
158
159
private void oCounter_NumberReached(object sender, NumberReachedEventArgs e)
160
{
161
MessageBox.Show("Reached: " + e.ReachedNumber.ToString());
162
}
163
private void oCounter_NumberReached2(object sender, NumberReachedEventArgs e)
164
{
165
MessageBox.Show("Reached2: " + e.ReachedNumber.ToString());
166
}
167
168
private void btnRemoveDelegate_Click(object sender, System.EventArgs e)
169
{
170
oCounter.NumberReached -= new NumberReachedEventHandler(oCounter_NumberReached2);
171
oCounter.CountTo(Convert.ToInt32(txtCountTo.Text), Convert.ToInt32(txtReachable.Text));
172
}
173
}
174
}

2

3

4

5

6

7

8

9



10


11

12

13

14



15

16

17

18

19

20

21

22

23


24

25

26

27

28

29



30

31

32

33

34

35

36

37

38

39

40

41

42

43


44

45

46

47



48

49



50

51



52

53

54

55

56

57

58


59


60

61

62

63

64



65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129



130

131

132

133

134

135

136

137

138

139

140

141

142

143


144

145

146

147

148



149

150

151

152

153



154

155

156

157

158

159

160



161

162

163

164



165

166

167

168

169



170

171

172

173

174

1
using
System;
2
3
namespace
Events
4
{
5
public delegate void NumberReachedEventHandler(object sender, NumberReachedEventArgs e);
6
7
/**//**//**//// <summary>
8
/// Summary description for Counter.
9
/// </summary>
10
public class Counter
11
{
12
public event NumberReachedEventHandler NumberReached;
13
14
public Counter()
15
{
16
//
17
// TODO: Add constructor logic here
18
//
19
}
20
public void CountTo(int countTo, int reachableNum)
21
{
22
if(countTo < reachableNum)
23
throw new ArgumentException("reachableNum should be less than countTo");
24
for(int ctr=0;ctr<=countTo;ctr++)
25
{
26
if(ctr == reachableNum)
27
{
28
NumberReachedEventArgs e = new NumberReachedEventArgs(reachableNum);
29
OnNumberReached(e);
30
return;//don't count any more
31
}
32
}
33
}
34
35
protected virtual void OnNumberReached(NumberReachedEventArgs e)
36
{
37
if(NumberReached!=null)
38
{
39
NumberReached(this, e);
40
}
41
}
42
}
43
44
public class NumberReachedEventArgs : EventArgs
45
{
46
private int _reached;
47
public NumberReachedEventArgs(int num)
48
{
49
this._reached = num;
50
}
51
public int ReachedNumber
52
{
53
get
54
{
55
return _reached;
56
}
57
}
58
}
59
}
60

2

3

4



5

6

7


8

9

10

11



12

13

14

15



16

17

18

19

20

21



22

23

24

25



26

27



28

29

30

31

32

33

34

35

36



37

38



39

40

41

42

43

44

45



46

47

48



49

50

51

52



53

54



55

56

57

58

59

60
