• 【转】五、谈扩展方法的理解


    【转】五、谈扩展方法的理解

    为什么要用扩展方法

    在说什么是扩展方法之前我们先来说说为什么要用扩展方法。

    首先我们定义一个 Person 类:

    public class Person
    {
        /// <summary>
        /// 出生日期
        /// </summary>
        public DateTime BirthTime { get; set; }
        /// <summary>
        /// 死亡日期
        /// </summary>
        public DateTime? DeathTime { get; set; }
        //、、、、、、
    }

    加入这个类来自第三方的dll引用,且现在我们需要添加一个方法 GetAge 获取年龄。你可能会想到自己定一个子类继承:

    public class MyPerson : Person
    {
        public int GetAge()
        {
            if (DeathTime.HasValue)
                return (DeathTime.Value - BirthTime).Days / 365;
            else
                return (DateTime.Now - BirthTime).Days / 365;
        }
    }

    是的,这样可以实现我们的需求。不过实现新增的方法就去继承真的是最合适的吗(暂且不说)? 如果上面定义的密封类呢? public sealed class Person ,这个时候是不能继承的,我们只能另想办法。

    随意写个静态类:

    public static class ExtensionClass
    {
        public static int GetAge(Person person)
        {
            if (person.DeathTime.HasValue)
                return (person.DeathTime.Value - person.BirthTime).Days / 365;
            else
                return (DateTime.Now - person.BirthTime).Days / 365;
        }

    然后调用  age = ExtensionClass.GetAge(p); ,是的看似不错。可是这和我们说的扩展方法有什么关系呢?下面就是见证奇迹的时候了。

    其他的任何地方都不变,唯一变化的是在参数前面加里this关键字。对,是的,仅仅如此它就变成了我们今天要讲的扩展方法。

    调用如:  var age = p.GetAge(); 相比上面的 age = ExtensionClass.GetAge(p); 更简单明了。

    这里我们说的是在需要扩展密封类的方法时,我们可以使用到扩展方法。还有一种情况就是,在需要扩展接口的时候时候我们更加需要。比如,需要扩展IList的排序。我们要么写个扩展方法,要么是继承实现接口(会强制要求实现接口下的所有方法)。我想你心中已经有了答案选择哪种方式。

    扩展方法到底是什么

    我们看到上面使用的扩展方法,有没有感觉很神奇。仅仅多添加了一个this关键字就直接可以当成扩展方法使用了。那扩展方法到底是什么东东,看了上面代码好像和静态方法有着说不清道不明的关系。下面我们继续分析:

    分别定义一个静态方法和一个扩展方法

     public static class ExtensionClass
     {
         public static int GetAge2(Person person)
         {
             if (person.DeathTime.HasValue)
                 return (person.DeathTime.Value - person.BirthTime).Days / 365;
             else
                 return (DateTime.Now - person.BirthTime).Days / 365;
         }
    
         public static int GetAge(this Person person)
         {
             if (person.DeathTime.HasValue)
                 return (person.DeathTime.Value - person.BirthTime).Days / 365;
             else
                 return (DateTime.Now - person.BirthTime).Days / 365;
         }

    分别调用:

    var p = new Person() { BirthTime = DateTime.Parse("1990-07-19") };
    var age = p.GetAge();
    age = ExtensionClass.GetAge2(p);

    编译后的IL代码:

    我们看到反编译成IL之后发现两者并无不同。所以,我理解成(扩展方法本质上就是静态方法,之所以出现扩展方法是C#以另外一种形式表现静态方法而已。只有有何妙用下面会继续讲解)。且 编译后同样带上了静态类名。

    扩展方法可以做些什么

    • 把已有的静态方法转成扩展方法:如:
    public static bool IsNullOrEmpty(this string str)
    {
        return string.IsNullOrEmpty(str);
    }

    调用: 

    string str = null;
    var isNull = str.IsNullOrEmpty();

     感觉相比期静态方法调用要优雅,更接近我们的自然语言。

    •  可以编写很多的帮助类,如(以string为例):
    /// <summary>
            /// 转DateTime 
            /// </summary>
            /// <param name="str"></param>
            /// <returns></returns>
            public static DateTime? MyToDateTime(this string str)
            {
                if (string.IsNullOrEmpty(str))
                    return null;
                else
                    return DateTime.Parse(str);
            }
    
            /// <summary>
            /// 转double
            /// </summary>
            /// <param name="str"></param>
            /// <returns></returns>
            public static double MyToDouble(this string str)
            {
                if (string.IsNullOrEmpty(str))
                    return -1;
                else
                    return double.Parse(str);
            }
    
            /// <summary>
            /// 转int
            /// </summary>
            /// <param name="str"></param>
            /// <returns></returns>
            public static int MyToInt(this string str)
            {
                if (string.IsNullOrEmpty(str))
                    return -1;
                else
                    return int.Parse(str);
            }
    
            /// <summary>
            /// 指示指定的字符串是 null 还是 System.String.Empty 字符串。
            /// </summary>
            /// <param name="str"></param>
            /// <returns></returns>
            public static bool IsNullOrEmpty(this string str)
            {
                return string.IsNullOrEmpty(str);
            }
    
            /// <summary>
            /// 如果字符串为null,则返回空字符串。(否则返回原字符串)
            /// </summary>
            /// <param name="str"></param>
            /// <returns></returns>
            public static string GetValueOrEmpty(this string str)
            {
                if (str.IsNullOrEmpty())
                    return string.Empty;
                return str;
            }
    View Code

    上面所有的都只是扩展方法的附加用处,扩展方法真正的威力是为Linq服务的(主要体现于IEnumerable和IQueryable),实现链式编程。下面我们自己来实现所谓的链式编程:

    初始化 Person 集合。

    List<Person> persons = new List<Person>() 
    {
         new Person(){ BirthTime=DateTime.Parse("1990-01-19")},
         new Person(){ BirthTime=DateTime.Parse("1993-04-17")},
         new Person(){ BirthTime=DateTime.Parse("1992-07-19"), DeathTime=DateTime.Parse("2010-08-18")},
         new Person(){ BirthTime=DateTime.Parse("1990-03-14")},
         new Person(){ BirthTime=DateTime.Parse("1991-08-15")},
         new Person(){ BirthTime=DateTime.Parse("1993-07-29")},
         new Person(){ BirthTime=DateTime.Parse("1991-06-19")}
    };

    需求:1.查询活人。2.按出生日期排序

    public static class ExtensionClass
        {
            /// <summary>
            /// 按条件查询
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="list"></param>
            /// <param name="func"></param>
            /// <returns></returns>
            public static IList<T> MyWhere<T>(this IList<T> list, Func<T, bool> func)
            {
                List<T> newList = new List<T>();
                foreach (var item in list)
                {
                    if (func(item))
                        newList.Add(item);
                }
                return newList;
            }
    
            /// <summary>
            /// 升序排序
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="list"></param>
            /// <param name="func"></param>
            /// <returns></returns>
            public static IList<T> MyOrderBy<T>(this IList<T> list, Func<T, DateTime> func)
            {
                if (list.Count() <= 1)
                    return list;
    
                for (int i = 0; i < list.Count(); i++)
                {
                    for (int j = i + 1; j < list.Count(); j++)
                    {
                        var item1 = list[j - 1];
                        var item2 = list[j];
                        if ((func(item1) - func(item2)).Ticks > 0)
                        {
                            list[j - 1] = item2;
                            list[j] = item1;
                        }
                    }
                }
                return list;
            }
            /// <summary>
            /// 降序排序
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="list"></param>
            /// <param name="func"></param>
            /// <returns></returns>
            public static IList<T> MyOrderByDescending<T>(this IList<T> list, Func<T, DateTime> func)
            {
                if (list.Count() <= 1)
                    return list;
    
                for (int i = 0; i < list.Count(); i++)
                {
                    for (int j = 1; j < list.Count() - i; j++)
                    {
                        var item1 = list[j - 1];
                        var item2 = list[j];
                        if ((func(item1) - func(item2)).Ticks < 0)
                        {
                            list[j - 1] = item2;
                            list[j] = item1;
                        }
                    }
                }
                return list;
            }
        }

    调用:(这里仅仅为了演示,所以不要讨论实现是否合理、算法是否高效。

    var newPersons = persons.MyWhere(t => t.DeathTime == null).MyOrderByDescending(t => t.BirthTime);
    foreach (var item in newPersons)
    {
        Console.WriteLine(item.BirthTime);
    }

    就是如此简单的实现了所谓的函数式编程。结果图如下:

    这样一句代码搞定所有逻辑,像自然语言般的流畅。其实.net为IEnumerable实现了这样的扩展,如:

    执行结构和上面一模一样。

    其实扩展方法也可以当成静态方法来使用:

     var p1 = ExtensionClass.MyWhere(persons, t => t.DeathTime == null);
     var p2 = ExtensionClass.MyOrderByDescending(p1, t => t.BirthTime);
     var p3 = ExtensionClass.MyOrderBy(p2, t => t.BirthTime);

    (不信?继续看,有图有真相)

    C#代码:

     

    反编译C#的代码:(你是不是看到了,编译后直接就是使用的扩展方法的形式。

    反编译的IL代码:

    虽然编译后的代码是一样的,但是做为程序员的我们更喜欢哪种方式呢?

    总结:

    我们在对扩展方法的怎么使用疑惑或者忘记了规则的时候,我们不用去查找资料说:

    1. 第一个参数是要扩展或者要操作的类型,这称为"被扩展的类型"
    2. 为了指定扩展方法,要在被扩展的类型名称前面附加this修饰符
    3. 要将方法作为一个扩展方法来访问,要用using指令导入扩展类型的命名空间,或者使扩展类型和调用代码在同一个命名空间中.

    我们只需记住,当你不知道怎么编写或使用扩展方法时,你先把它当成静态方法编写或使用。如果可行,一般都可以转成扩展方法的形式。

    全部代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Text;
    using System.Threading.Tasks;
    using System.Data.Entity.Utilities;
    using System.Diagnostics.CodeAnalysis;
    using NPOI.HSSF.UserModel;
    using NPOI.SS.UserModel;
    using System.IO;
    
    namespace test
    {
    
    
        class Program
        {
            static void Main(string[] args)
            {
                /*             
                 * 1.工具类
                 * 2.链式编程
                 */
                string str = null;
                var isNull = str.IsNullOrEmpty();
    
                var p = new Person() { BirthTime = DateTime.Parse("1990-07-19") };
                var age = p.GetAge();
                age = ExtensionClass.GetAge2(p);
    
                List<Person> persons = new List<Person>() 
                {
                     new Person(){ BirthTime=DateTime.Parse("1990-01-19")},
                     new Person(){ BirthTime=DateTime.Parse("1993-04-17")},
                     new Person(){ BirthTime=DateTime.Parse("1992-07-19"), DeathTime=DateTime.Parse("2010-08-18")},
                     new Person(){ BirthTime=DateTime.Parse("1990-03-14")},
                     new Person(){ BirthTime=DateTime.Parse("1991-08-15")},
                     new Person(){ BirthTime=DateTime.Parse("1993-07-29")},
                     new Person(){ BirthTime=DateTime.Parse("1991-06-19")}
                };
    
                var newPersons = persons.MyWhere(t => t.DeathTime == null).MyOrderByDescending(t => t.BirthTime);
    
    
                var p1 = ExtensionClass.MyWhere(persons, t => t.DeathTime == null);
                var p2 = ExtensionClass.MyOrderByDescending(p1, t => t.BirthTime);
                var p3 = ExtensionClass.MyOrderBy(p2, t => t.BirthTime);
    
                foreach (var item in newPersons)
                {
                    Console.WriteLine(item.BirthTime);
                }
                Console.ReadKey();
            }
        }
    
        public sealed class Person
        {
            /// <summary>
            /// 出生日期
            /// </summary>
            public DateTime BirthTime { get; set; }
            /// <summary>
            /// 死亡日期
            /// </summary>
            public DateTime? DeathTime { get; set; }
        }
    
        //public class MyPerson : Person
        //{
        //    public int GetAge()
        //    {
        //        if (DeathTime.HasValue)
        //            return (DeathTime.Value - BirthTime).Days / 365;
        //        else
        //            return (DateTime.Now - BirthTime).Days / 365;
        //    }
        //}
        public static class ExtensionClass
        {
            /// <summary>
            /// 按条件查询
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="list"></param>
            /// <param name="func"></param>
            /// <returns></returns>
            public static IList<T> MyWhere<T>(this IList<T> list, Func<T, bool> func)
            {
                List<T> newList = new List<T>();
                foreach (var item in list)
                {
                    if (func(item))
                        newList.Add(item);
                }
                return newList;
            }
    
            /// <summary>
            /// 升序排序
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="list"></param>
            /// <param name="func"></param>
            /// <returns></returns>
            public static IList<T> MyOrderBy<T>(this IList<T> list, Func<T, DateTime> func)
            {
                if (list.Count() <= 1)
                    return list;
    
                for (int i = 0; i < list.Count(); i++)
                {
                    for (int j = i + 1; j < list.Count(); j++)
                    {
                        var item1 = list[j - 1];
                        var item2 = list[j];
                        if ((func(item1) - func(item2)).Ticks > 0)
                        {
                            list[j - 1] = item2;
                            list[j] = item1;
                        }
                    }
                }
                return list;
            }
            /// <summary>
            /// 降序排序
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="list"></param>
            /// <param name="func"></param>
            /// <returns></returns>
            public static IList<T> MyOrderByDescending<T>(this IList<T> list, Func<T, DateTime> func)
            {
                if (list.Count() <= 1)
                    return list;
    
                for (int i = 0; i < list.Count(); i++)
                {
                    for (int j = 1; j < list.Count() - i; j++)
                    {
                        var item1 = list[j - 1];
                        var item2 = list[j];
                        if ((func(item1) - func(item2)).Ticks < 0)
                        {
                            list[j - 1] = item2;
                            list[j] = item1;
                        }
                    }
                }
                return list;
            }
    
            public static int GetAge2(Person person)
            {
                if (person.DeathTime.HasValue)
                    return (person.DeathTime.Value - person.BirthTime).Days / 365;
                else
                    return (DateTime.Now - person.BirthTime).Days / 365;
            }
    
            public static int GetAge(this Person person)
            {
                if (person.DeathTime.HasValue)
                    return (person.DeathTime.Value - person.BirthTime).Days / 365;
                else
                    return (DateTime.Now - person.BirthTime).Days / 365;
            }
    
            public static bool IsNullOrEmpty(this string str)
            {
                return string.IsNullOrEmpty(str);
            }
        } 
    }
    View Code

    本文以同步至《C#基础知识巩固系列

  • 相关阅读:
    面向对象深入:继承01——子类
    面向对象的基础知识——小结
    IP地址配置
    二、RPM包管理-rpm命令管理
    一、软件包管理简介
    关机重启命令
    网络命令
    权限管理命令
    字符截取命令
    shell-正则表达式(二)
  • 原文地址:https://www.cnblogs.com/RYouHoo-923/p/8267517.html
Copyright © 2020-2023  润新知