变态篇二中给出了对if/else、swith/case及while 的扩展,大家评价各不相同,其实本人也感觉有点牵强。其中举了一个Swith扩展的应用,今天突然有了新想法,对它改进了一些。所谓“语不惊人死不休”,且看这次的改进如何。
我先把扩展的源代码贴出来,折叠一下,等看完后面的例子和讲解再回来看。 (和前面一样,本文侧重想法,代码演示用,如需使用,请自行完善)
首先看这些扩展的一个最简单的应用,如下:
这里解释一下,第2行中的lambda表达式:(string s)=>typeName = s 需要传入一个字符串参数。
第3~6行的Case在满足条件时将第二个参数“内部”返回,传给(string s)=>typeName = s。
这样来理解:当typeId为0时,Case返回“食品”并传入给lambda,为1时返回“饮料”...
最终lambda将值赋给了typeName。
有点绕,一定按这个思路去想,否则下面就不好明白了。
把上面的代码“展开”,相当于以下代码 (有点长,折叠起来好一些):
再来看一个复杂点的应用,用户注册时经常要检验用户密码的强度,通常用不同颜色展示给用户,让用户有个直观的了解。
这里我们简化一下,仅判断密码的长度,越长认为安全性越好。用红的背景色展示给用户,密码越不安全越红,反之则红色变淡。
通俗点,密码长红色淡,密码短红色深。
下面根据密码长度获取背景颜色:
同理第4行的lambda将,Case返回的颜色赋给backColor。
第5、6行Case中第一个参数是一个lambda,Case扩展即可以与一个实际值比较相等,也可以判断范围。
仔细看这段代码,Color.FromArgbp被调用了很多次,我们来重构一下:
到现在为止估计大家应该有一个疑问了,原来的switch/case中可以使用“break”直接返回,这里是怎么处理的呢?
Case还有第三个参数,它用来处理实是否break,为true时break,false时继续下一个Case。
个人感觉大多数情况下,符合某个条件后一般不需要继续其它的了,所以重载传入true,即默认break。
与switch/case是相反的。如果不习惯,你可以在扩展的源代码中修改一下!
我们再看一个非break的情形如何使用,应用场景如下:
一款关于球的游戏:
进球6~10个(包含6、10,以下同),可得奖励 1;
进球11~20,再奖励 10;
进球21~50,再奖励 100;
进球51~100,再奖励 1000;
进球超过100,再奖励 10000;
例:进球30个,奖励为 1+10+100 = 111。
写个函数计算奖励。
多个Case链起来使用不一定最后一个参数全为false,可以如下调用:
这里也使用了 “链式编程”(概念参见我的相关文章),它们是如何串起来的。我们看下这几个扩展的定义:
Swith扩展返回SwithCase<TCase, TOther>,这是一简单的泛型类,如下:
我们使用SwithCase泛型类把这两“值”封装起来,作为Switch的返回。
Case和Default是对SwithCase 泛型类作的扩展,Case的返回值也是SwithCase泛型类。
通过SwitchCase泛型类,我们就串起来了, 以Swith开始,以Default结束(Default没返回值,Default也可以省略)。
SwichCase泛型类用作这里链式编程的链接有以下好处。
1.平时Case和Default扩展是隐藏的,不会出现在代码智能提示中(因为它是对SwitchCase作的扩展)。
2.只有在Switch扩展的智能提示中,才有Case和Default,才可以使用。
基于以上特性,我称之为 “组扩展” :它们是一组,只有使用了组长(Switch)后,才能使用组员。
“组扩展”的优势在于对其它代码的“污染”小(只有一个显示在智能提示中),也避免了直接调用组成员的非法操作。
组扩展先说到这里,我们再来看下Swith的两个参数:Func<TInput, TCase> selector和Action<TOther> action。
第一参数可对传入的实例进行一些处理(调用属性、方法或返回一个新的对象),第二个参数是一个Action<T>可以封闭复杂的操作。
有了这两个参数,Swith可以非常灵活。远不只以上几个应用,只是现在还没发掘出来。
大刀可以杀敌人,也可以削苹果,关键在谁手中,怎么用。
顺便提一下,Switch扩展还可以再加入新的参数,来消减GetReward中的多个“c => c >”,前面的源码中没有给出实现,感兴趣可自己尝试一下。
有一点说明一下,第一个例子中Case(1, "饮料"),默认为typeId为1时自动break,但是在链式编程中是无法忽略后面的Case直接返回的。
现在的处理是判断成功后,返回null值给下一个Case, 下一个Case什么也不执行再向下传递null值...直到最后一个。
这样处理会带来一定的性能损失(很小很小),这是链式编程的缺点,无法解决。
这个方法思路上有点怪,性能也有损失,但它确实能减少代码量(是否易于书写和维护另说)。
每个人想法不同,思路相差很大,不知道这里的扩展是否适合你。也不知道你能否接收 “组扩展”的概念。
有想法写在回复中,本人喜欢探讨问题,可以接受任何反对意见。
本人系列文章《 c#扩展方法奇思妙用》,敬请关注!
我先把扩展的源代码贴出来,折叠一下,等看完后面的例子和讲解再回来看。 (和前面一样,本文侧重想法,代码演示用,如需使用,请自行完善)
SwithCaseExtension
1 public static class SwithCaseExtension
2 {
3 SwithCase#region SwithCase
4 public class SwithCase<TCase, TOther>
5 {
6 public SwithCase(TCase value, Action<TOther> action)
7 {
8 Value = value;
9 Action = action;
10 }
11 public TCase Value { get; private set; }
12 public Action<TOther> Action { get; private set; }
13 }
14 #endregion
15
16 Swith#region Swith
17 public static SwithCase<TCase, TOther> Switch<TCase, TOther>(this TCase t, Action<TOther> action) where TCase : IEquatable<TCase>
18 {
19 return new SwithCase<TCase, TOther>(t, action);
20 }
21
22 public static SwithCase<TCase, TOther> Switch<TInput, TCase, TOther>(this TInput t, Func<TInput, TCase> selector, Action<TOther> action) where TCase : IEquatable<TCase>
23 {
24 return new SwithCase<TCase, TOther>(selector(t), action);
25 }
26 #endregion
27
28 Case#region Case
29 public static SwithCase<TCase, TOther> Case<TCase, TOther>(this SwithCase<TCase, TOther> sc, TCase option, TOther other) where TCase : IEquatable<TCase>
30 {
31 return Case(sc, option, other, true);
32 }
33
34
35 public static SwithCase<TCase, TOther> Case<TCase, TOther>(this SwithCase<TCase, TOther> sc, TCase option, TOther other, bool bBreak) where TCase : IEquatable<TCase>
36 {
37 return Case(sc, c=>c.Equals(option), other, bBreak);
38 }
39
40
41 public static SwithCase<TCase, TOther> Case<TCase, TOther>(this SwithCase<TCase, TOther> sc, Predicate<TCase> predict, TOther other) where TCase : IEquatable<TCase>
42 {
43 return Case(sc, predict, other, true);
44 }
45
46 public static SwithCase<TCase, TOther> Case<TCase, TOther>(this SwithCase<TCase, TOther> sc, Predicate<TCase> predict, TOther other, bool bBreak) where TCase : IEquatable<TCase>
47 {
48 if (sc == null) return null;
49 if (predict(sc.Value))
50 {
51 sc.Action(other);
52 return bBreak ? null : sc;
53 }
54 else return sc;
55 }
56 #endregion
57
58 Default#region Default
59 public static void Default<TCase, TOther>(this SwithCase<TCase, TOther> sc, TOther other)
60 {
61 if (sc == null) return;
62 sc.Action(other);
63 }
64 #endregion
65 }
这段代码定义了三个扩展Switch、Case和Default。
1 public static class SwithCaseExtension
2 {
3 SwithCase#region SwithCase
4 public class SwithCase<TCase, TOther>
5 {
6 public SwithCase(TCase value, Action<TOther> action)
7 {
8 Value = value;
9 Action = action;
10 }
11 public TCase Value { get; private set; }
12 public Action<TOther> Action { get; private set; }
13 }
14 #endregion
15
16 Swith#region Swith
17 public static SwithCase<TCase, TOther> Switch<TCase, TOther>(this TCase t, Action<TOther> action) where TCase : IEquatable<TCase>
18 {
19 return new SwithCase<TCase, TOther>(t, action);
20 }
21
22 public static SwithCase<TCase, TOther> Switch<TInput, TCase, TOther>(this TInput t, Func<TInput, TCase> selector, Action<TOther> action) where TCase : IEquatable<TCase>
23 {
24 return new SwithCase<TCase, TOther>(selector(t), action);
25 }
26 #endregion
27
28 Case#region Case
29 public static SwithCase<TCase, TOther> Case<TCase, TOther>(this SwithCase<TCase, TOther> sc, TCase option, TOther other) where TCase : IEquatable<TCase>
30 {
31 return Case(sc, option, other, true);
32 }
33
34
35 public static SwithCase<TCase, TOther> Case<TCase, TOther>(this SwithCase<TCase, TOther> sc, TCase option, TOther other, bool bBreak) where TCase : IEquatable<TCase>
36 {
37 return Case(sc, c=>c.Equals(option), other, bBreak);
38 }
39
40
41 public static SwithCase<TCase, TOther> Case<TCase, TOther>(this SwithCase<TCase, TOther> sc, Predicate<TCase> predict, TOther other) where TCase : IEquatable<TCase>
42 {
43 return Case(sc, predict, other, true);
44 }
45
46 public static SwithCase<TCase, TOther> Case<TCase, TOther>(this SwithCase<TCase, TOther> sc, Predicate<TCase> predict, TOther other, bool bBreak) where TCase : IEquatable<TCase>
47 {
48 if (sc == null) return null;
49 if (predict(sc.Value))
50 {
51 sc.Action(other);
52 return bBreak ? null : sc;
53 }
54 else return sc;
55 }
56 #endregion
57
58 Default#region Default
59 public static void Default<TCase, TOther>(this SwithCase<TCase, TOther> sc, TOther other)
60 {
61 if (sc == null) return;
62 sc.Action(other);
63 }
64 #endregion
65 }
首先看这些扩展的一个最简单的应用,如下:
1
string
typeName
=
string
.Empty;
2 typeId.Switch(( string s) => typeName = s)
3 .Case( 0 , " 食品 " )
4 .Case( 1 , " 饮料 " )
5 .Case( 2 , " 酒水 " )
6 .Case( 3 , " 毒药 " )
7 .Default( " 未知 " );
输入一个整数,返回它表示的含义。
(很多方法可以解决这个问题,此处示例,请勿较真!)
2 typeId.Switch(( string s) => typeName = s)
3 .Case( 0 , " 食品 " )
4 .Case( 1 , " 饮料 " )
5 .Case( 2 , " 酒水 " )
6 .Case( 3 , " 毒药 " )
7 .Default( " 未知 " );
这里解释一下,第2行中的lambda表达式:(string s)=>typeName = s 需要传入一个字符串参数。
第3~6行的Case在满足条件时将第二个参数“内部”返回,传给(string s)=>typeName = s。
这样来理解:当typeId为0时,Case返回“食品”并传入给lambda,为1时返回“饮料”...
最终lambda将值赋给了typeName。
有点绕,一定按这个思路去想,否则下面就不好明白了。
把上面的代码“展开”,相当于以下代码 (有点长,折叠起来好一些):
Code
1 string typeName = string.Empty;
2 switch (typeId)
3 {
4 case 0:
5 typeName = "食品";
6 break;
7 case 1:
8 typeName = "食品";
9 break;
10 case 2:
11 typeName = "酒水";
12 break;
13 case 3:
14 typeName = "毒药";
15 break;
16 default:
17 typeName = "未知";
18 break;
19 }
代码行数比较一下吧,前面的是7行,这儿是19行。
1 string typeName = string.Empty;
2 switch (typeId)
3 {
4 case 0:
5 typeName = "食品";
6 break;
7 case 1:
8 typeName = "食品";
9 break;
10 case 2:
11 typeName = "酒水";
12 break;
13 case 3:
14 typeName = "毒药";
15 break;
16 default:
17 typeName = "未知";
18 break;
19 }
再来看一个复杂点的应用,用户注册时经常要检验用户密码的强度,通常用不同颜色展示给用户,让用户有个直观的了解。
这里我们简化一下,仅判断密码的长度,越长认为安全性越好。用红的背景色展示给用户,密码越不安全越红,反之则红色变淡。
通俗点,密码长红色淡,密码短红色深。
下面根据密码长度获取背景颜色:
1
private
static
Color GetBackColor(
string
password)
2 {
3 Color backColor = default (Color);
4 password.Switch(p => p.Length, (Color c) => backColor = c)
5 .Case(l => l <= 4 , Color.FromArgb( 255 , 0 , 0 ))
6 .Case(l => l <= 6 , Color.FromArgb( 255 , 63 , 63 ))
7 .Case( 7 , Color.FromArgb( 255 , 127 , 127 ))
8 .Case( 8 , Color.FromArgb( 255 , 191 , 191 ))
9 .Default(Color.FromArgb( 255 , 255 , 255 ));
10 return backColor;
11 }
先看Switch的这里有两个参数,在第一个位置插入了一个参数,它取了密码的长度来进行比较。
2 {
3 Color backColor = default (Color);
4 password.Switch(p => p.Length, (Color c) => backColor = c)
5 .Case(l => l <= 4 , Color.FromArgb( 255 , 0 , 0 ))
6 .Case(l => l <= 6 , Color.FromArgb( 255 , 63 , 63 ))
7 .Case( 7 , Color.FromArgb( 255 , 127 , 127 ))
8 .Case( 8 , Color.FromArgb( 255 , 191 , 191 ))
9 .Default(Color.FromArgb( 255 , 255 , 255 ));
10 return backColor;
11 }
同理第4行的lambda将,Case返回的颜色赋给backColor。
第5、6行Case中第一个参数是一个lambda,Case扩展即可以与一个实际值比较相等,也可以判断范围。
仔细看这段代码,Color.FromArgbp被调用了很多次,我们来重构一下:
1
private
static
Color GetBackColor2(
string
password)
2 {
3 Color backColor = default (Color);
4 password.Switch(p => p.Length, ( int red) => backColor = Color.FromArgb( 255 , 256 - red, 256 - red))
5 .Case(l => l <= 4 , 256 )
6 .Case(l => l <= 6 , 192 )
7 .Case( 7 , 128 )
8 .Case( 8 , 64 )
9 .Default( 0 );
10 return backColor;
11 }
这样看起来简单了吧,当然Color.FromArgb也可以放在return中。这里是故意放在了Switch的第二个参数中的!
2 {
3 Color backColor = default (Color);
4 password.Switch(p => p.Length, ( int red) => backColor = Color.FromArgb( 255 , 256 - red, 256 - red))
5 .Case(l => l <= 4 , 256 )
6 .Case(l => l <= 6 , 192 )
7 .Case( 7 , 128 )
8 .Case( 8 , 64 )
9 .Default( 0 );
10 return backColor;
11 }
到现在为止估计大家应该有一个疑问了,原来的switch/case中可以使用“break”直接返回,这里是怎么处理的呢?
Case还有第三个参数,它用来处理实是否break,为true时break,false时继续下一个Case。
个人感觉大多数情况下,符合某个条件后一般不需要继续其它的了,所以重载传入true,即默认break。
与switch/case是相反的。如果不习惯,你可以在扩展的源代码中修改一下!
我们再看一个非break的情形如何使用,应用场景如下:
一款关于球的游戏:
进球6~10个(包含6、10,以下同),可得奖励 1;
进球11~20,再奖励 10;
进球21~50,再奖励 100;
进球51~100,再奖励 1000;
进球超过100,再奖励 10000;
例:进球30个,奖励为 1+10+100 = 111。
写个函数计算奖励。
1
private
static
int
GetReward(
int
count)
2 {
3 int score = 0 ;
4 count.Switch(( int i) => score += i)
5 .Case(c => c > 5 , 1 , false )
6 .Case(c => c > 10 , 10 , false )
7 .Case(c => c > 20 , 100 , false )
8 .Case(c => c > 50 , 1000 , false )
9 .Case(c => c > 100 , 10000 , false );
10 return score;
11 }
Case的最后一个参数false表示符合条件后不break,继续下一个Case。
2 {
3 int score = 0 ;
4 count.Switch(( int i) => score += i)
5 .Case(c => c > 5 , 1 , false )
6 .Case(c => c > 10 , 10 , false )
7 .Case(c => c > 20 , 100 , false )
8 .Case(c => c > 50 , 1000 , false )
9 .Case(c => c > 100 , 10000 , false );
10 return score;
11 }
多个Case链起来使用不一定最后一个参数全为false,可以如下调用:
1
int
i
=
5
;
2 int j = 0 ;
3 i.Switch(( int k) => j += i * i)
4 .Case( 1 , 1 , true )
5 .Case( 2 , 2 , false )
6 .Case( 3 , 1 )
7 .Case( 4 , 2 , true )
8 .Default( 5 );
这段代码没实际意思,只是为了说明可以像swith/case那样使用。
2 int j = 0 ;
3 i.Switch(( int k) => j += i * i)
4 .Case( 1 , 1 , true )
5 .Case( 2 , 2 , false )
6 .Case( 3 , 1 )
7 .Case( 4 , 2 , true )
8 .Default( 5 );
这里也使用了 “链式编程”(概念参见我的相关文章),它们是如何串起来的。我们看下这几个扩展的定义:
1
public
static
SwithCase
<
TCase, TOther
>
Switch
<
TInput, TCase, TOther
>
2 ( this TInput t, Func < TInput, TCase > selector, Action < TOther > action)
3 where TCase : IEquatable < TCase > {}
4
5 public static SwithCase < TCase, TOther > Case < TCase, TOther >
6 ( this SwithCase < TCase, TOther > sc, Predicate < TCase > predict, TOther other, bool bBreak)
7 where TCase : IEquatable < TCase > {}
8
9 public static void Default < TCase, TOther > ( this SwithCase < TCase, TOther > sc, TOther other){}
Swith扩展可用于任意类型,任意类型的实例都可以调用Swith。
2 ( this TInput t, Func < TInput, TCase > selector, Action < TOther > action)
3 where TCase : IEquatable < TCase > {}
4
5 public static SwithCase < TCase, TOther > Case < TCase, TOther >
6 ( this SwithCase < TCase, TOther > sc, Predicate < TCase > predict, TOther other, bool bBreak)
7 where TCase : IEquatable < TCase > {}
8
9 public static void Default < TCase, TOther > ( this SwithCase < TCase, TOther > sc, TOther other){}
Swith扩展返回SwithCase<TCase, TOther>,这是一简单的泛型类,如下:
1
public
class
SwithCase
<
TCase, TOther
>
2 {
3 public SwithCase(TCase value, Action < TOther > action)
4 {
5 Value = value;
6 Action = action;
7 }
8 public TCase Value { get ; private set ; }
9 public Action < TOther > Action { get ; private set ; }
10 }
因为Case需要“判断
的值”和“判断成功的处理”,也就说需要两个“值”(确切说是一个值和一个委托)。
2 {
3 public SwithCase(TCase value, Action < TOther > action)
4 {
5 Value = value;
6 Action = action;
7 }
8 public TCase Value { get ; private set ; }
9 public Action < TOther > Action { get ; private set ; }
10 }
我们使用SwithCase泛型类把这两“值”封装起来,作为Switch的返回。
Case和Default是对SwithCase 泛型类作的扩展,Case的返回值也是SwithCase泛型类。
通过SwitchCase泛型类,我们就串起来了, 以Swith开始,以Default结束(Default没返回值,Default也可以省略)。
SwichCase泛型类用作这里链式编程的链接有以下好处。
1.平时Case和Default扩展是隐藏的,不会出现在代码智能提示中(因为它是对SwitchCase作的扩展)。
2.只有在Switch扩展的智能提示中,才有Case和Default,才可以使用。
基于以上特性,我称之为 “组扩展” :它们是一组,只有使用了组长(Switch)后,才能使用组员。
“组扩展”的优势在于对其它代码的“污染”小(只有一个显示在智能提示中),也避免了直接调用组成员的非法操作。
组扩展先说到这里,我们再来看下Swith的两个参数:Func<TInput, TCase> selector和Action<TOther> action。
第一参数可对传入的实例进行一些处理(调用属性、方法或返回一个新的对象),第二个参数是一个Action<T>可以封闭复杂的操作。
有了这两个参数,Swith可以非常灵活。远不只以上几个应用,只是现在还没发掘出来。
大刀可以杀敌人,也可以削苹果,关键在谁手中,怎么用。
顺便提一下,Switch扩展还可以再加入新的参数,来消减GetReward中的多个“c => c >”,前面的源码中没有给出实现,感兴趣可自己尝试一下。
有一点说明一下,第一个例子中Case(1, "饮料"),默认为typeId为1时自动break,但是在链式编程中是无法忽略后面的Case直接返回的。
现在的处理是判断成功后,返回null值给下一个Case, 下一个Case什么也不执行再向下传递null值...直到最后一个。
这样处理会带来一定的性能损失(很小很小),这是链式编程的缺点,无法解决。
这个方法思路上有点怪,性能也有损失,但它确实能减少代码量(是否易于书写和维护另说)。
每个人想法不同,思路相差很大,不知道这里的扩展是否适合你。也不知道你能否接收 “组扩展”的概念。
有想法写在回复中,本人喜欢探讨问题,可以接受任何反对意见。
本人系列文章《 c#扩展方法奇思妙用》,敬请关注!