zoukankan      html  css  js  c++  java
  • c# 扩展方法奇思妙用变态篇二:封装 if/else、swith/case 及 while

    本文探讨如何使用扩展方法封装 if/else、swith/case及while,通过使用这些扩展,写出的代码将使用很少的大括号{ }。扩展的效果如何,还请大家来评判!
    声明:本文属于(改)变(形)态篇,只是提出一种想法,所提供的代码也只是示例,可以测试通过,但不完善。

    首先我们来对看if/else和swith/case,两者在代码中都用来表达分支结构。这里我们统一封装成一个If扩展:

            public static T If<T>(this T t, Predicate<T> predicate, Action<T> action) where T: class
            {
                
    if(t == nullthrow new ArgumentNullException();
                
    if (predicate(t)) action(t);
                
    return t;
            }

     看下面的调用代码,生成一个People的实例,让他吃饱喝足休息好再工作:

    复制代码
            public static void Test1()
            {
                
    //常规代码
                People people1 = new People { Name = "ldp615", IsHungry = true, IsThirsty = true, IsTired = true };
                
    if (people1.IsHungry) people1.Eat();
                
    if (people1.IsThirsty) people1.Drink();
                
    if (people1.IsTired) people1.Rest();
                people1.Work();
                
    //使用扩展方法
                People people2 = new People { Name = "ldp615", IsHungry = true, IsThirsty = true, IsTired = true }
                    .If(p 
    => p.IsHungry, p => p.Eat())
                    .If(p 
    => p.IsThirsty, p => p.Drink())
                    .If(p 
    => p.IsTired, p => p.Rest());
                people2.Work();
            }
    复制代码

    扩展方法中的If可以使用点“.”链起来,称之“链式编程”,请参见我我随笔《c#链式编程》。
    常规代码和使用扩展方法写的代码都在上面,大家比较一下吧。
    使用If扩展的代码中用了lambda表达式,如果前面的“p=>p.”能去掉的话,看起来就比较舒服了!编译器通过类型及上下文推演,应该可以做得到吧!
    给出People类如下:

     


            public class People
            {
                
    public string Name { getset; }
                
    public bool IsHungry { getset; }
                
    public bool IsThirsty { getset; }
                
    public bool IsTired { getset; }
                
    public int WorkCount { getprivate set; }

                
    public void Eat()
                {
                    Console.WriteLine(
    "Eat");
                    IsHungry 
    = false;
                }
                
    public void Drink()
                {
                    Console.WriteLine(
    "Drink");
                    IsThirsty 
    = false;
                }
                
    public void Rest()
                {
                    Console.WriteLine(
    "Rest");
                    IsTired 
    = false;
                }
                
    public void Work()
                {
                    Console.WriteLine(
    "Work");
                    IsHungry 
    = IsThirsty = IsTired = true;
                    WorkCount
    ++;
                }
            }

     


    对引用类型我们可以使用Action<T>,也以使用链式编程的方式将多个If串起来。
    但对值类型来说,就要用Func<T, T>了,每次返回一个新的值 :

            public static T If<T>(this T t, Predicate<T> predicate, Func<T, T> func) where T : struct
            {
                
    return predicate(t) ? func(t) : t;
            }

    调用代码也要修改:

    复制代码
            public static void Test2()
            {
                
    //扩展方式
                int int0 = -121;
                
    int int1 = int0.If(i => i < 0, i => -i)
                    .If(i 
    => i > 100, i => i - 100)
                    .If(i 
    => i % 2 == 1, i => i - 1);
                
    //常规方式
                int int3 = -121;
                
    if (int3 < 0) int3 = -int3;
                
    if (int3 > 100) int3 -= 100;
                
    if (int3 % 2 == 1) int3--;
            }
    复制代码


    引用类型及值类型的扩展我们已经完成,用string来测试一下吧,如下:

    复制代码
            public static void Test3()
            {
                
    //从邮箱变换成主页
                string email = "ldp615@163.com";
                
    string page = email.If(s => s.Contains("@"), s => s.Substring(0, s.IndexOf("@")))
                    .If(s 
    =>! s.StartsWith("www."), s => s = "www." + s)
                    .If(s 
    =>! s.EndsWith(".com"), s => s += ".com");
            }
    复制代码

    但编译不通过,会提示错误:

    这个错误比较怪,我们写了两个扩展,一个是给值类型的,一个给引用类型,可string类型在这里都不行。这个原因我说不清楚了,还留给园子里高手们吧。
    不过专门为string写个扩展,这个问题可以化解,如下:

            public static string If(this string s, Predicate<string> predicate, Func<stringstring> func)
            {
                
    return predicate(s) ? func(s) : s;
            }

    看来扩展方法也是有优先级的:对同一个类进行多次扩展,扩展方法相名,参数也等效(数量、顺序相同),非泛版扩展要比泛型版扩展优先级高。

    下面再来看一段swith代码,很啰嗦的!这里是为了引出扩展硬写出来的:

    复制代码
            public static void Test4()
            {
                
    string englishName = "apple";
                
    string chineseName = string.Empty;
                
    switch (englishName)
                {
                    
    case "apple":
                        chineseName 
    = "苹果";
                        
    return;
                    
    case "orange":
                        chineseName 
    = "桔子";
                        
    return;
                    
    case "banana":
                        chineseName 
    = "香蕉";
                        
    return;
                    
    case "pear":
                        chineseName 
    = "";
                        
    break;
                    
    default:
                        chineseName 
    = "未知";
                        
    break;
                }
                Console.WriteLine(chineseName);
            }
    复制代码

    我们把这种方式用扩展方法来完成:

    复制代码
            public static  TOutput Switch<TOutput, TInput>(this TInput input, IEnumerable<TInput> inputSource, IEnumerable<TOutput> outputSource, TOutput defaultOutput)
            {
                IEnumerator
    <TInput> inputIterator = inputSource.GetEnumerator();
                IEnumerator
    <TOutput> outputIterator = outputSource.GetEnumerator();

                TOutput result 
    = defaultOutput;
                
    while (inputIterator.MoveNext())
                {
                    
    if (outputIterator.MoveNext())
                    {
                        
    if (input.Equals(inputIterator.Current))
                        {
                            result 
    = outputIterator.Current;
                            
    break;
                        }
                    }
                    
    else break;
                }
                
    return result;
            }
    复制代码

    下面的Test5是调用:

    复制代码
            public static void Test5()
            {
                
    string englishName = "apple";
                
    string chineseName = englishName.Switch(
                    
    new string[] { "apple""orange""banana""pear" },
                    
    new string[] { "苹果""桔子""香蕉""" },
                    
    "未知"
                    );
                Console.WriteLine(chineseName);
            }
    复制代码

    简单清晰明了!

    最后是一个对while的扩展封装:

            public static void While<T>(this T t,  Predicate<T> predicate, Action<T> action) where T: class
            {
                
    while (predicate(t)) action(t);
            }

    调用代码:

    复制代码
            public static void Test6()
            {
                People people 
    = new People { Name = "Wretch" };
                people.While(
                    p 
    => p.WorkCount < 7,
                    p 
    => p.Work()
                        );
                people.Rest();
            }
    复制代码

    这里又“召唤”了一个人,不让吃喝不让休息,连续工作7次...
    这while扩展中只能执行一个Action<T>,不太好,我们用params改进一下:

    复制代码
            public static void While<T>(this T t, Predicate<T> predicate, params Action<T>[] actions) where T : class
            {
                
    while (predicate(t))
                {
                    
    foreach (var action in actions)
                        action(t);
                }
            }
    复制代码

    再来调用,可以在循环中执行多个操作了,这次舒服工作完吃饭喝水休息,再来工作...

    复制代码
            public static void Test7()
            {
                People people 
    = new People { Name = "Wretch" };
                people.While(
                    p 
    => p.WorkCount < 7,
                    p 
    => p.Work(),
                    p 
    => p.Eat(),
                    p 
    => p.Drink(),
                    p 
    => p.Rest()
                        );
                people.Rest();
            }
    复制代码

    当然前面的If也可以这样的,这里只写出一个:


            public static T If<T>(this T t, Predicate<T> predicate, params Action<T>[] actions) where T : class
            {
                
    if (t == nullthrow new ArgumentNullException();
                
    if (predicate(t))
                {
                    
    foreach (var action in actions)
                        action(t);
                }
                
    return t;
            }

    不使用 params,你就要显示声明一个Action<T>的集合了!关于params, 在我的随笔《改进 Scottgu 的 "In" 扩展 》有说明。

  • 相关阅读:
    UVA 1513
    《ArcGIS Runtime SDK for Android开发笔记》——问题集:.geodatabase创建,创建时内容缺失问题总结
    《ArcGIS Runtime SDK for Android开发笔记》——问题集:使用TextSymbol做标注显示乱码
    《ArcGIS Runtime SDK for Android开发笔记》——(7)、示例代码arcgis-runtime-samples-android的使用
    《ArcGIS Runtime SDK for Android开发笔记》——(6)、基于Android Studio的ArcGIS Android工程结构解析
    《ArcGIS Runtime SDK for Android开发笔记》——(5)、基于Android Studio构建ArcGIS Android开发环境(离线部署)
    《ArcGIS Runtime SDK for Android开发笔记》——(4)、基于Android Studio构建ArcGIS Android开发环境
    《ArcGIS Runtime SDK for Android开发笔记》——(3)、ArcGIS Runtime SDK概述
    《ArcGIS Runtime SDK for Android开发笔记》——(2)、Android Studio基本配置与使用
    《ArcGIS Runtime SDK for Android开发笔记》——(1)、Android Studio下载与安装
  • 原文地址:https://www.cnblogs.com/ywsoftware/p/3128807.html
Copyright © 2011-2022 走看看