当针对不同的类型采取相同的算法时,我们想到了泛型。当根据相同的参数列表,采取不同的算法时,我们可以考虑使用委托。就用委托演绎中国男足进入2018年世界杯吧。
对球的不同处理方式,抽象成如下方法:
1: private static void PassBall(string ball)
2: {
3: Console.Write("传" + ball);
4: }
5:
6: private static void StopBall(string ball)
7: {
8: Console.Write("停/接" + ball);
9: }
10:
11: private static void TransferpBall(string ball)
12: {
13: Console.Write("转移" + ball);
14: }
15:
16: private static void RunBall(string ball)
17: {
18: Console.Write("带" + ball);
19: }
20:
21: private static void ScoreBallByHead(string ball)
22: {
23: Console.Write("头"+ball + "射门");
24: }
25:
26: private static void ScoreBallByInnerFeet(string ball)
27: {
28: Console.Write("推射" + ball + "射门");
29: }
30:
31: private static void ScoreBallByOuterFeet(string ball)
32: {
33: Console.Write("外脚背射" + ball + "门");
34: }
发现尽管方法名,方法体内的实现逻辑不一样,但参数列表是一样的,都是针对球的,委托登场。
public delegate void PlayBall(string ball);
委托从本质上来讲就是一个类,所以也可以把委托作为方法参数。
private static void Game(string ball, PlayBall playBallDelegate)
{
playBallDelegate(ball);
}
主程序中先把各种方法注册到委托,然后在Game方法中使用不同的委托所代表的方法,控制球的处理。
1: static void Main(string[] args)
2: {
3: PlayBall del;
4: del = PassBall;
5: del += StopBall;
6: del += TransferpBall;
7: del += RunBall;
8: del += ScoreBallByHead;
9: del += ScoreBallByInnerFeet;
10: del += ScoreBallByOuterFeet;
11:
12: string str = "欢迎大家来到中国队进军2018年俄罗斯世界杯的生死战,本场比赛,中国队只有获胜才能拿到世界杯的入场券,比赛进行到90分钟,中国队与韩国队的比分依然是0:0,补时3分钟。最后关头,小伙子们加油啊!";
13:
14: Console.WriteLine(str);
15: Console.WriteLine();
16: Console.WriteLine();
17:
18: Console.Write("中后卫冯潇霆");
19: Game("球",PassBall);
20: Console.Write("给了队长郑智");
21: Console.WriteLine();
22:
23: Console.Write("郑智");
24: Game("球",TransferpBall);
25: Console.Write("给了右边路后插上的张琳芃");
26: Console.WriteLine();
27:
28: Console.Write("张琳芃");
29: Game("球",RunBall);
30: Console.Write("过了韩国队2名后卫,把球交给了张稀哲");
31: Console.WriteLine();
32:
33: Console.Write("张稀哲稳稳地");
34: Game("球",StopBall);
35: Console.Write(",把球交给了无人防守的武磊");
36: Console.WriteLine();
37:
38: Console.Write("武磊");
39: Game("",ScoreBallByOuterFeet);
40: Console.Write(",韩国队守门员奋力扑出");
41: Console.WriteLine();
42:
43: Console.Write("球落到了左边路孙祥的脚下");
44: Console.Write(",孙祥传中");
45: Console.WriteLine();
46:
47: Console.Write("后点郜林");
48: Game("球",ScoreBallByHead);
49: Console.Write(", 球进了!");
50: Console.WriteLine();
51: Console.WriteLine();
52: Console.WriteLine();
53: Console.WriteLine("进了!进了!进了!伟大的中国队前锋!他继承了中国队光荣的传统。郝海东、李金羽、高峰在这一刻灵魂附体!郜林一个人他代表了中国足球悠久的历史和传统,在这一刻他不是一个人在战斗,他不是一个人! ");
54: Console.ReadKey();
55: }
□ 委托也可以不使用+=
PlayBall del1 = PassBall;
PlayBall del2 = StopBall;
Game("球",del1);
Game("球",del2);
□ 委托赋值是用=,委托绑定是用+=,两者不能混淆
如果以上来就:
PlayBall del1 += PassBall;
这样会报"使用了未赋值的局部变量"错,因为委托变量还没有赋值,正所谓"皮之不存毛将焉附"。
□ 可以通过带参数的委托构造函数为委托变量赋值
PlayBall del1 = new PlayBall(PassBall);
del1 += StopBall;
□ 不可以通过不带参数的委托构造函数为委托变量赋值
PlayBall del1 = new PlayBall();
这样会报"没有0个参数的重载"的错。
□ 也可以取消委托中的某个方法的绑定
PlayBall del1 = new PlayBall(PassBall);
del1 += StopBall;
del1 += ScoreBallByHead;
del1 -= StopBall;
□ 接口与委托
共同点:
隔离了变化。
不同点:
接口不仅参数列表一样,方法名也一样。
接口可能更适合单元测试。
体验:
可能在小范围之内,使用委托比较好。
一旦涉及到分层、扩展、单元测试,可能使用接口比较好。
参考资料:
※ 《.NET之美》--张子阳,感谢写了这么好的书!