5、谈扩展方法的理解

为何要用扩展方法

在说什么是扩展方法以前咱们先来讲说为何要用扩展方法。html

首先咱们定义一个 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 ,这个时候是不能继承的,咱们只能另想办法。编程

随意写个静态类:ide

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

扩展方法究竟是什么

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

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

 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#基础知识巩固系列

相关文章
相关标签/搜索