12事件
继续上一篇:11委托演绎中国足球队打进世界杯
□ 含有相同参数列表的各种处理球方法不变,把带委托参数的方法放到一个类中
1: namespace ConsoleApplication11
2: {
3: class Program
4: {
5: static void Main(string[] args)
6: {
7: GameManager gm = new GameManager();
8: gm.Game("球",PassBall);
9: Console.WriteLine();
10: gm.Game("球",TransferpBall);
11: Console.ReadKey();
12: }
13:
14: private static void PassBall(string ball)
15: {
16: Console.Write("传" + ball);
17: }
18:
19: private static void StopBall(string ball)
20: {
21: Console.Write("停/接" + ball);
22: }
23:
24: private static void TransferpBall(string ball)
25: {
26: Console.Write("转移" + ball);
27: }
28:
29: private static void RunBall(string ball)
30: {
31: Console.Write("带" + ball);
32: }
33:
34: private static void ScoreBallByHead(string ball)
35: {
36: Console.Write("头"+ball + "射门");
37: }
38:
39: private static void ScoreBallByInnerFeet(string ball)
40: {
41: Console.Write("推射" + ball + "射门");
42: }
43:
44: private static void ScoreBallByOuterFeet(string ball)
45: {
46: Console.Write("外脚背射" + ball + "门");
47: }
48: }
49: public delegate void PlayBall(string ball);
50:
51: public class GameManager
52: {
53: public void Game(string ball, PlayBall playBallDelegate)
54: {
55: playBallDelegate(ball);
56: }
57: }
58: }
□ 采用多播委托,把多个方法绑定到同一个委托变量
1: GameManager gm = new GameManager();
2: PlayBall del = PassBall;
3: del += RunBall;
4: gm.Game("球",del);
5: Console.ReadKey();
□ 把委托封装到类中
1: class Program
2: {
3: static void Main(string[] args)
4: {
5: GameManager gm = new GameManager();
6: gm.delegate1 = ScoreBallByHead;
7: gm.delegate1 += ScoreBallByInnerFeet;
8: gm.Game("球", gm.delegate1);
9: Console.ReadKey();
10: }
11: .....
12: }
13: public delegate void PlayBall(string ball);
14:
15: public class GameManager
16: {
17: public PlayBall delegate1;
18: public void Game(string ball, PlayBall playBallDelegate)
19: {
20: playBallDelegate(ball);
21: }
22: }
还可以把gm.Game("球", gm.delegate1)改成类似gm.Game("球"),达到这样的效果,需要在Game()方法体内让委托变量来执行方法。
□ 直接调用类的实例方法,不带委托参数
1: class Program
2: {
3: static void Main(string[] args)
4: {
5: GameManager gm = new GameManager();
6: gm.delegate1 = PassBall;
7: gm.delegate1 += ScoreBallByOuterFeet;
8: gm.Game("球");
9: Console.ReadKey();
10: }
11: ......
12: }
13: public delegate void PlayBall(string ball);
14:
15: public class GameManager
16: {
17: public PlayBall delegate1;
18: public void Game(string ball)
19: {
20: if (delegate1 != null)
21: {
22: delegate1(ball);
23: }
24: }
25: }
26:
依然存在的问题是:
1、把委托字段成delegate1声明成public,客户端可以随意赋值,破坏了对象的封闭原则。
2、如果把委托字段成delegate1声明成private,客户端就不能调用。
3、给委托赋值用=,给委托注册用+=,好像也不是很统一。
□ 事件就此登上历史舞台
1: class Program
2: {
3: static void Main(string[] args)
4: {
5: GameManager gm = new GameManager();
6: gm.event1 = PassBall;
7: gm.event1 += ScoreBallByOuterFeet;
8: gm.Game("球");
9: Console.ReadKey();
10: }
11: ......
12: }
13: public delegate void PlayBall(string ball);
14:
15: public class GameManager
16: {
17: public event PlayBall event1;
18: public void Game(string ball)
19: {
20: event1(ball);
21: }
22: }
23:
事件可以看成:
为委托类型量身定制的属性。
使用事件的好处:
限制含有事件类型的能力,即事件应该由事件的发布者触发,而不应该由事件的客户端来触发。
□ 事件的一个例子
1: namespace ConsoleApplication12
2: {
3: class Program
4: {
5: static void Main(string[] args)
6: {
7: Publisher publisher = new Publisher(2);
8: Subscriber subscriber = new Subscriber();
9:
10: publisher.NumberChanged += new NumberChangedEventHandler(subscriber.OnNumberChanged);
11: publisher.DoSth();
12: Console.ReadKey();
13: }
14: }
15:
16: //定义委托
17: public delegate void NumberChangedEventHandler(int count);
18:
19: //事件发布者
20: public class Publisher
21: {
22: private int count;
23: public event NumberChangedEventHandler NumberChanged;
24:
25: public Publisher(int count)
26: {
27: this.count = count;
28: }
29:
30: public void DoSth()
31: {
32: Console.WriteLine("我是发布者,我开始工作了~~");
33: if (NumberChanged != null)
34: {
35: count++;
36: NumberChanged(count);
37: }
38: }
39: }
40:
41: //定义事件订阅者
42: public class Subscriber
43: {
44: public void OnNumberChanged(int count)
45: {
46: Console.WriteLine("我知道了,现在的数量是:" + count);
47: }
48: }
49: }
50:
关于事件的小结:
● 在委托的前面加上关键字event,就变成了事件public event NumberChangedEventHandler NumberChanged
● 一旦声明成事件,只能通过事件所在类(发布者)的方法来触发事件:publisher.DoSth();
订阅者的和发布者的惯例:
● 如果想运行订阅者的方法触发事件public delegate void NumberChangedEventHandler(int count),从而让订阅者也触发相应的方法,那订阅者的方法OnNumberChanged(int count)的参数列表和事件一致,并且是在事件变量NumberChanged前面加上On变成OnNumberChanged。
● 订阅者是通过publisher.NumberChanged += new NumberChangedEventHandler(subscriber.OnNumberChanged)绑定到发布者的事件,建立触发关联的。
参考资料:
※ 《.NET之美》--张子阳,感谢写了这么好的书!