你的位置:首页 > ASP.net教程

[ASP.net教程]【备战面试之】五、谈扩展方法的理解


为什么要用扩展方法

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

首先我们定义一个 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);

 

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

全部代码:

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

 

 

以上都是胡说八道。

好了,今天的扩展方法就分析到这里。感谢阅读,希望对您有一点点作用!

文章首链:http://www.cnblogs.com/zhaopei/p/5678842.html