在說什麼是擴展方法以前咱們先來講說爲何要用擴展方法。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();
感受相比期靜態方法調用要優雅,更接近咱們的天然語言。
/// <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; }
上面全部的都只是擴展方法的附加用處,擴展方法真正的威力是爲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代碼:
雖然編譯後的代碼是同樣的,可是作爲程序員的咱們更喜歡哪一種方式呢?
總結:
咱們在對擴展方法的怎麼使用疑惑或者忘記了規則的時候,咱們不用去查找資料說:
咱們只需記住,當你不知道怎麼編寫或使用擴展方法時,你先把它當成靜態方法編寫或使用。若是可行,通常均可以轉成擴展方法的形式。
所有代碼:
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); } } }
本文以同步至《C#基礎知識鞏固系列》