zoukankan      html  css  js  c++  java
  • c#扩展方法奇思妙用变态篇三:switch/case组扩展

    变态篇二中给出了对if/else、swith/case及while 的扩展,大家评价各不相同,其实本人也感觉有点牵强。其中举了一个Swith扩展的应用,今天突然有了新想法,对它改进了一些。所谓“语不惊人死不休”,且看这次的改进如何。

    我先把扩展的源代码贴出来,折叠一下,等看完后面的例子和讲解再回来看。(和前面一样,本文侧重想法,代码演示用,如需使用,请自行完善)


     1   public static class SwithCaseExtension
     2    {
     3        #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 getprivate set; }
    12            public Action<TOther> Action getprivate set; }
    13        }

    14        #endregion

    15
    16        #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        #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 == nullreturn 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        #region Default
    59        public static void Default<TCase, TOther>(this SwithCase<TCase, TOther> sc, TOther other)
    60        {
    61            if (sc == nullreturn;
    62            sc.Action(other);
    63        }

    64        #endregion

    65    }

    这段代码定义了三个扩展Switch、Case和Default。
    首先看这些扩展的一个最简单的应用,如下:

    复制代码
    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行中的lambda表达式:(string s)=>typeName = s 需要传入一个字符串参数。
    第3~6行的Case在满足条件时将第二个参数“内部”返回,传给(string s)=>typeName = s。
    这样来理解:当typeId为0时,Case返回“食品”并传入给lambda,为1时返回“饮料”...
    最终lambda将值赋给了typeName。
    有点绕,一定按这个思路去想,否则下面就不好明白了。

    把上面的代码“展开”,相当于以下代码(有点长,折叠起来好一些)


     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     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(25500))
     6             .Case(l => l <= 6, Color.FromArgb(2556363))
     7             .Case(7, Color.FromArgb(255127127))
     8             .Case(8, Color.FromArgb(255191191))
     9             .Default(Color.FromArgb(255255255));
    10         return backColor;
    11     }
    复制代码

    先看Switch的这里有两个参数,在第一个位置插入了一个参数,它取了密码的长度来进行比较。
    同理第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(255256 - red, 256 - red))
     5             .Case(l => l <= 4256)
     6             .Case(l => l <= 6192)
     7             .Case(7128)
     8             .Case(864)
     9             .Default(0);
    10         return backColor;
    11     }
    复制代码

    这样看起来简单了吧,当然Color.FromArgb也可以放在return中。这里是故意放在了Switch的第二个参数中的!

    到现在为止估计大家应该有一个疑问了,原来的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 > 51false)
     6             .Case(c => c > 1010false)
     7             .Case(c => c > 20100false)
     8             .Case(c => c > 501000false)
     9             .Case(c => c > 10010000false);
    10         return score;
    11     }
    复制代码

    Case的最后一个参数false表示符合条件后不break,继续下一个Case。

    多个Case链起来使用不一定最后一个参数全为false,可以如下调用:

    复制代码
    1     int i = 5;
    2     int j = 0;
    3     i.Switch((int k) => j += i * i)
    4         .Case(11true)
    5         .Case(22false)
    6         .Case(31)
    7         .Case(42true)
    8         .Default(5);
    复制代码

    这段代码没实际意思,只是为了说明可以像swith/case那样使用。

    这里也使用了链式编程”(概念参见我的相关文章),它们是如何串起来的。我们看下这几个扩展的定义:

    复制代码
    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。
    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 { getprivate set; }
     9         public Action<TOther> Action { getprivate set; }
    10     }
    复制代码

    因为Case需要“判断的值”和“判断成功的处理”,也就说需要两个“值”(确切说是一个值和一个委托)。
    我们使用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值...直到最后一个。
    这样处理会带来一定的性能损失(很小很小),这是链式编程的缺点,无法解决。

    这个方法思路上有点怪,性能也有损失,但它确实能减少代码量(是否易于书写和维护另说)。
    每个人想法不同,思路相差很大,不知道这里的扩展是否适合你。也不知道你能否接收“组扩展”的概念。
    有想法写在回复中,本人喜欢探讨问题,可以接受任何反对意见。

  • 相关阅读:
    NSData和NSString 、 NSFileManager
    通知(Notification) 、 应用间通信(一)
    Dynamic支持CollectionView布局 、 MotionEffects特效 、 BlurImage效果 、 TextKit
    Dynamic Animator 、 CollectionViewLayout
    动画(Animation) 、 高级动画(Core Animation)
    Autoresizing和AutoLayout
    触控(Touch) 、 布局(Layout)
    手势与变形 、 视图与坐标系
    常用的静态变量,包括颜色等
    JSPatch动态修改已上架app的bug,即时修复bug
  • 原文地址:https://www.cnblogs.com/ywsoftware/p/3128811.html
Copyright © 2011-2022 走看看