-
C#
本次的内容,主要是针对已经学习过C# 2.0的程序员读者的,前提是已经知道什么是匿名方法。如果还不清楚,请先阅读“连载:C# 2.0入门”(这篇会在今后翻译)。
好的,现在进入正题。
Lambda表达式(λ表达式),用一句话来解释,(不算很严谨)就是使匿名方法文字上更短的语法。虽然这样说,单“仅仅是文字上的变化,源代码的性质没有变”这样的想法也是一种误解。规模变化了,其性质也会变化。例如,实验室的烧杯中产生的现象,不一定会在大型的工厂里产生。同样的道理也适用于源代码。
那么,就体验一下Lambda表达式吧。
以下,使用具体的code来说明一下,不是实际工程中的代码,而是实际代码使用C# 2.0重写的。
前一阵子笔者实际写的code中,有一个菜单,能够选择的下拉菜单。菜单项是下面这样定义的:
1public delegate bool SimpleMenuAction();
2
3public class MenuItemA // 菜单项
4{
5 public readonly string Name; // 名字
6 public readonly SimpleMenuAction Action; //执行内容
7
8 public MenuItemA(string name, SimpleMenuAction action)
9 {
10 Name = name;
11 Action = action;
12 }
13}
14
List 1 菜单项的定义
与之相对应,以下是菜单项的数组。
1private static MenuItemA[] Menu Items1 =
2 {
3 new MenuItemA("选择项1", 执行方法),
4 new MenuItemA("选择项2", 执行方法),
5 new MenuItemA("选择项3", 执行方法),
6 };
7
编缉推荐阅读以下文章
- [C# 3.0 入门] [第一章 Lambda表达式] 第五节:Lambda表达式的使用范例 & Lambda表达式的各种变体
- [C# 3.0 入门] [第一章 Lambda表达式] 第四节:Lambda的用途 & 类型声明能够和不能够省略的情况
- [C# 3.0 入门] [第一章 Lambda表达式] 第三节:Lambda与匿名方法的区别
- [C# 3.0 入门] [第一章 Lambda表达式] 第一节:C# 3.0 是什么 & C# 3.0的适用范围
- C# 3.0 入门系列(一)
List 2 菜单项目数组
实际上,当时认为这样就足够了,谁知中途又被要求加入一种菜单项,这种菜单项在19点以后才可以看到。如果只有这一个的话,用if语句括起来判断一下例外条件就能够处理,可是要求是2个,而且还可能增加。于是,就想在这个表中添加条件语句。
最simple的解决方案,应该就是在MenuItem类里,保存“几点以后有效”的“几点”的整数值。
首先在MenuItemA类中,加上保存时间的整数字段“FromHour”。
1public class MenuItemB
2{
3 public readonly string Name;
4 public readonly SimpleMenuAction Action;
5 public readonly int FromHour;
6
7 public MenuItemB(string name, SimpleMenuAction action, int fromHour)
8 {
9 Name = name;
10 Action = action;
11 FromHour = fromHour;
12 }
13}
14
List 3 加上时间字段的菜单项定义
菜单项数组也改写成以下:
1private static MenuItemB[] MenuItems2 =
2 {
3 new MenuItemB("选择项1", 执行方法, 0),
4 new MenuItemB("选择项2", 执行方法, 0),
5 new MenuItemB("选择项3", 执行方法, 0),
6 new MenuItemB("选择项4", 执行方法, 19),
7 };
8
List 4 对List 3的菜单项进行修正
这样,需要的信息都能够包含在数组里了。
构建菜单的方法是,通过检查被选择的菜单对象的FromHour的值,如果与现在时间相比小,就把该菜单项显示出来。
编缉推荐阅读以下文章
- [C# 3.0 入门] [第一章 Lambda表达式] 第五节:Lambda表达式的使用范例 & Lambda表达式的各种变体
- [C# 3.0 入门] [第一章 Lambda表达式] 第四节:Lambda的用途 & 类型声明能够和不能够省略的情况
- [C# 3.0 入门] [第一章 Lambda表达式] 第三节:Lambda与匿名方法的区别
- [C# 3.0 入门] [第一章 Lambda表达式] 第一节:C# 3.0 是什么 & C# 3.0的适用范围
- C# 3.0 入门系列(一)
这么看来,这个code如果按照YAGNI*的原则来看的话就比较完善了,这样的代码也属于良品了。
* YAGNI是“You Aren't Going to Need It.”的简写,意思是:或许是必要的功能实际上并不必要的可能性非常高。一句话,为未知的未来而事先准备的代码,基本上是没用的。这样的教训很多。
但是,这个代码来应对需求变更的要求,恐怕还太脆弱。例如,条件如果从19点改为19点半,就没法办了。或者要求设置个结束时间,或者是个时间段的话,或是根据星期几而变动,这种要求实在是太多了。
琢磨了一下要求,加入能够指定条件的代码,修正后如下:
1public delegate bool SimpleMenuAvailability();
2
3public class MenuItemC
4{
5 public readonly string Name;
6 public readonly SimpleMenuAction Action;
7 // 判断现在是否是有效菜单项
8 public readonly SimpleMenuAvailability IsAvailable;
9
10 public MenuItemC(string name, SimpleMenuAction action, SimpleMenuAvailability isAvailable)
11 {
12 Name = name;
13 Action = action;
14 IsAvailable = isAvailable;
15 }
16}
17
List5 加上条件的菜单定义
如果使用匿名方法,数组就要改写成如下:
Menu
1private static MenuItemC[] MenuItems3 =
2 {
3 new MenuItemC(
4 "选择1", 执行方法, delegate() { return true; }),
5 new MenuItemC(
6 "选择2", 执行方法, delegate() { return true; }),
7 new MenuItemC(
8 "选择3", 执行方法, delegate() { return true; }),
9 new MenuItemC(
10 "选择4", 执行方法, delegate() { return DateTime.Now.Hour >= 19; } ),
11 };
12
编缉推荐阅读以下文章
- [C# 3.0 入门] [第一章 Lambda表达式] 第五节:Lambda表达式的使用范例 & Lambda表达式的各种变体
- [C# 3.0 入门] [第一章 Lambda表达式] 第四节:Lambda的用途 & 类型声明能够和不能够省略的情况
- [C# 3.0 入门] [第一章 Lambda表达式] 第三节:Lambda与匿名方法的区别
- [C# 3.0 入门] [第一章 Lambda表达式] 第一节:C# 3.0 是什么 & C# 3.0的适用范围
- C# 3.0 入门系列(一)
List 6 List 5中对应的菜单项
可是,这样的代码如果要为将来可能需要也可能不需要的变化准备的话,将会变得相当臃肿。本质上完全没有意义的delegate和return显得非常刺眼,看上去很难理解意图。这种代码就是YAGNI原则所说的那种应该避免的代码的典型例子。
因此,如果使用C# 2.0,这种code应该就不会被采用。笔者虽然属于那种对匿名方法使用得挥金如土的类型,在这个case上,(用匿名方法)恐怕优势要小于劣势。
然而,下面这并不是匿名方法,打眼一看就是较少的文字就能描述的Lambda表达式。下面的代码,“()=>true”和“()=>DateTime.Now.Hour > 19”,一看就是Lambda表达式的样子。
1private static MenuItemC[] MenuItems4 =
2 {
3 new MenuItemC("选择项1", 执行方法, ()=>true),
4 new MenuItemC("选择项2", 执行方法, ()=>true),
5 new MenuItemC("选择项3", 执行方法, ()=>true),
6 new MenuItemC("选择项4", 执行方法, ()=>DateTime.Now.Hour >= 19),
7 };
8
List 7 对List 6改用Lambda表达式后
说实话,code写成这样应该可以通过了。虽然违反了YAGNI的原则,但在不损害code的可理解性的范围内,对于未知修改来说也是上了保险了。
事实上这个保险也确实起作用。很快,菜单的有效时间从“19点以后”变成“19点后22点前”。来吧,数组做如下修正:
1private static MenuItemC[] MenuItems5 =
2 {
3 new MenuItemC("选择项1", 执行方法, ()=>true),
4 new MenuItemC("选择项2", 执行方法, ()=>true),
5 new MenuItemC("选择项3", 执行方法, ()=>true),
6 new MenuItemC("选择项4", 执行方法,
7 ()=>DateTime.Now.Hour >= 19 && DateTime.Now.Hour < 22),
8 };
9
List 8 List 7中第四个菜单项的修改
这个变更,只用改写一个Lambda表达式这样的局部变更就能搞定,一瞬间的事。但是,如果采用最初的List 4的code,菜单项类还要加上结束时间,菜单的构建方法中还要加上判断,相当的费事。然而,不仅如此,如果不是Lambda表达式,而是使用匿名方法的前提下,或许这种费事的方法也会得到采用。总之,匿名方法与Lambda表达式的长度上的差别对code会有质的影响。
编缉推荐阅读以下文章
- [C# 3.0 入门] [第一章 Lambda表达式] 第五节:Lambda表达式的使用范例 & Lambda表达式的各种变体
- [C# 3.0 入门] [第一章 Lambda表达式] 第四节:Lambda的用途 & 类型声明能够和不能够省略的情况
- [C# 3.0 入门] [第一章 Lambda表达式] 第三节:Lambda与匿名方法的区别
- [C# 3.0 入门] [第一章 Lambda表达式] 第一节:C# 3.0 是什么 & C# 3.0的适用范围
- C# 3.0 入门系列(一)
List 2 菜单项目数组
实际上,当时认为这样就足够了,谁知中途又被要求加入一种菜单项,这种菜单项在19点以后才可以看到。如果只有这一个的话,用if语句括起来判断一下例外条件就能够处理,可是要求是2个,而且还可能增加。于是,就想在这个表中添加条件语句。
最simple的解决方案,应该就是在MenuItem类里,保存“几点以后有效”的“几点”的整数值。
首先在MenuItemA类中,加上保存时间的整数字段“FromHour”。
1public class MenuItemB
2{
3 public readonly string Name;
4 public readonly SimpleMenuAction Action;
5 public readonly int FromHour;
6
7 public MenuItemB(string name, SimpleMenuAction action, int fromHour)
8 {
9 Name = name;
10 Action = action;
11 FromHour = fromHour;
12 }
13}
14
List 3 加上时间字段的菜单项定义
菜单项数组也改写成以下:
1private static MenuItemB[] MenuItems2 =
2 {
3 new MenuItemB("选择项1", 执行方法, 0),
4 new MenuItemB("选择项2", 执行方法, 0),
5 new MenuItemB("选择项3", 执行方法, 0),
6 new MenuItemB("选择项4", 执行方法, 19),
7 };
8
List 4 对List 3的菜单项进行修正
这样,需要的信息都能够包含在数组里了。
构建菜单的方法是,通过检查被选择的菜单对象的FromHour的值,如果与现在时间相比小,就把该菜单项显示出来。