為何要用擴展方法?
作為一個.NET程序猿,我們經常要跟.net自帶類庫或者第三方dll類庫打交道,有時候我們未必能夠通過反編譯來查看它們的代碼,但是我們通常需要給它們擴充一些新的功能,Helper類就應運而生了,我們開發出一個個的靜態方法以方便調用。久而久之,我們封裝的Helper類越來越多,但是這個Helper里邊的方法不一定為每個開發人員所熟知,而且我們每每要敲擊XXXHelper.XXX()這種類似的方法,其實這個XXXHelper完全是可以省略掉的,等於是我們每次都多寫了這么一點東西。
有沒有解決辦法呢?擴展方法就是解決這個問題的。擴展方法使您能夠向現有類型“添加”方法,而無需創建新的派生類型、重新編譯或以其他方式修改原始類型。它可以對現有類功能進行擴充,從而使該類型的實例具有更多的方法,偉大的LINQ就是用的這個東西實現的。
使用擴展方法的好處:
1、讓方法調用回歸面向對象本身,即方法是屬於對象的,增強代碼可讀性
2、從一定程度上來說擴展方法讓你的類庫有了無限的可能性
擴展方法的應用
擴展方法必須遵守以下規則:
1.擴展類必須是"靜態非泛型"的;
2.擴展方法必須是靜態的
3.擴展方法的第一個參數必須以this開頭,參數必須是原有類的類型
舉個擴展方法的栗子:
我們經常需要對對象進行序列化,那么我們可以給對象object擴展一個這樣的方法ToJSON
using Newtonsoft.Json; namespace Ctrip.Json
{ public static class JsonHelper { public static string ToJson(this object obj) { string JsonStr = JsonConvert.SerializeObject(obj); return JsonStr; } } }
調用:
using Ctrip.Json; Student stu=new Student{Name="張三",Sex="男",Age=25}; string jsonString = stu.ToJson();
平時我們總是需要操作字符串類型,因此這里就送上一個string類型的擴展方法類庫:

using System; using System.Globalization; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using Infrastructure.Common; using Newtonsoft.Json; namespace Infrastructure.Utility { /// <summary> /// 字符串類型幫助類 /// </summary> public static class StringHelper { /// <summary> /// Object對象序列化為Json字串 /// </summary> /// <param name="obj"></param> /// <returns></returns> public static string ToJson(this object obj) { return JsonConvert.SerializeObject(obj); } /// <summary> /// Json字串反序列化為Object強類型 /// </summary> public static T DeJson<T>(this string json) { return JsonConvert.DeserializeObject<T>(json); } /// <summary> /// 根據根據字符串轉換成相對應的枚舉子項 /// </summary> /// <typeparam name="TEnum">枚舉類型</typeparam> /// <param name="strEnumDescription">字符串</param> /// <returns>枚舉子項</returns> public static TEnum ToEnum<TEnum>(this string strEnumDescription) where TEnum : struct { Type enumType = typeof(TEnum); TEnum[] enumValues = (TEnum[])Enum.GetValues(enumType); foreach (var enumValue in enumValues) { string strValue = enumValue.ToString(); FieldInfo fieldinfo = enumValue.GetType().GetField(strValue); object[] objs = fieldinfo.GetCustomAttributes(typeof(EnumItemAttribute), false); if (objs.Length == 0) { continue; } EnumItemAttribute enumItemAttribute = (EnumItemAttribute)objs[0]; if (enumItemAttribute != null && string.Equals(enumItemAttribute.Description, strEnumDescription, StringComparison.CurrentCultureIgnoreCase)) { return enumValue; } } return default(TEnum); } /// <summary> /// 獲得字符串的長度,一個漢字的長度為1 /// </summary> public static int GetStringLength(string s) { if (!string.IsNullOrEmpty(s)) return Encoding.Default.GetBytes(s).Length; return 0; } /// <summary> /// 獲得字符串中指定字符的個數 /// </summary> /// <param name="s">字符串</param> /// <param name="c">字符</param> /// <returns></returns> public static int GetCharCount(string s, char c) { if (s == null || s.Length == 0) return 0; int count = 0; foreach (char a in s) { if (a == c) count++; } return count; } /// <summary> /// 獲得指定順序的字符在字符串中的位置索引 /// </summary> /// <param name="s">字符串</param> /// <param name="order">順序</param> /// <returns></returns> public static int IndexOf(string s, int order) { return IndexOf(s, '-', order); } /// <summary> /// 獲得指定順序的字符在字符串中的位置索引 /// </summary> /// <param name="s">字符串</param> /// <param name="c">字符</param> /// <param name="order">順序</param> /// <returns></returns> public static int IndexOf(string s, char c, int order) { int length = s.Length; for (int i = 0; i < length; i++) { if (c == s[i]) { if (order == 1) return i; order--; } } return -1; } #region 分割字符串 /// <summary> /// 分割字符串 /// </summary> /// <param name="sourceStr">源字符串</param> /// <param name="splitStr">分隔字符串</param> /// <returns></returns> public static string[] SplitString(string sourceStr, string splitStr) { if (string.IsNullOrEmpty(sourceStr) || string.IsNullOrEmpty(splitStr)) return new string[0] { }; if (sourceStr.IndexOf(splitStr) == -1) return new string[] { sourceStr }; if (splitStr.Length == 1) return sourceStr.Split(splitStr[0]); else return Regex.Split(sourceStr, Regex.Escape(splitStr), RegexOptions.IgnoreCase); } /// <summary> /// 分割字符串 /// </summary> /// <param name="sourceStr">源字符串</param> /// <returns></returns> public static string[] SplitString(string sourceStr) { return SplitString(sourceStr, ","); } #endregion #region 截取字符串 /// <summary> /// 截取字符串 /// </summary> /// <param name="sourceStr">源字符串</param> /// <param name="startIndex">開始位置的索引</param> /// <param name="length">子字符串的長度</param> /// <returns></returns> public static string SubString(string sourceStr, int startIndex, int length) { if (!string.IsNullOrEmpty(sourceStr)) { if (sourceStr.Length >= (startIndex + length)) return sourceStr.Substring(startIndex, length); else return sourceStr.Substring(startIndex); } return ""; } /// <summary> /// 截取字符串 /// </summary> /// <param name="sourceStr">源字符串</param> /// <param name="length">子字符串的長度</param> /// <returns></returns> public static string SubString(string sourceStr, int length) { return SubString(sourceStr, 0, length); } #endregion #region 移除前導/后導字符串 /// <summary> /// 移除前導字符串 /// </summary> /// <param name="sourceStr">源字符串</param> /// <param name="trimStr">移除字符串</param> /// <returns></returns> public static string TrimStart(string sourceStr, string trimStr) { return TrimStart(sourceStr, trimStr, true); } /// <summary> /// 移除前導字符串 /// </summary> /// <param name="sourceStr">源字符串</param> /// <param name="trimStr">移除字符串</param> /// <param name="ignoreCase">是否忽略大小寫</param> /// <returns></returns> public static string TrimStart(string sourceStr, string trimStr, bool ignoreCase) { if (string.IsNullOrEmpty(sourceStr)) return string.Empty; if (string.IsNullOrEmpty(trimStr) || !sourceStr.StartsWith(trimStr, ignoreCase, CultureInfo.CurrentCulture)) return sourceStr; return sourceStr.Remove(0, trimStr.Length); } /// <summary> /// 移除后導字符串 /// </summary> /// <param name="sourceStr">源字符串</param> /// <param name="trimStr">移除字符串</param> /// <returns></returns> public static string TrimEnd(string sourceStr, string trimStr) { return TrimEnd(sourceStr, trimStr, true); } /// <summary> /// 移除后導字符串 /// </summary> /// <param name="sourceStr">源字符串</param> /// <param name="trimStr">移除字符串</param> /// <param name="ignoreCase">是否忽略大小寫</param> /// <returns></returns> public static string TrimEnd(string sourceStr, string trimStr, bool ignoreCase) { if (string.IsNullOrEmpty(sourceStr)) return string.Empty; if (string.IsNullOrEmpty(trimStr) || !sourceStr.EndsWith(trimStr, ignoreCase, CultureInfo.CurrentCulture)) return sourceStr; return sourceStr.Substring(0, sourceStr.Length - trimStr.Length); } /// <summary> /// 移除前導和后導字符串 /// </summary> /// <param name="sourceStr">源字符串</param> /// <param name="trimStr">移除字符串</param> /// <returns></returns> public static string Trim(string sourceStr, string trimStr) { return Trim(sourceStr, trimStr, true); } /// <summary> /// 移除前導和后導字符串 /// </summary> /// <param name="sourceStr">源字符串</param> /// <param name="trimStr">移除字符串</param> /// <param name="ignoreCase">是否忽略大小寫</param> /// <returns></returns> public static string Trim(string sourceStr, string trimStr, bool ignoreCase) { if (string.IsNullOrEmpty(sourceStr)) return string.Empty; if (string.IsNullOrEmpty(trimStr)) return sourceStr; if (sourceStr.StartsWith(trimStr, ignoreCase, CultureInfo.CurrentCulture)) sourceStr = sourceStr.Remove(0, trimStr.Length); if (sourceStr.EndsWith(trimStr, ignoreCase, CultureInfo.CurrentCulture)) sourceStr = sourceStr.Substring(0, sourceStr.Length - trimStr.Length); return sourceStr; } #endregion } }
備注:
1、在vs的智能提示中,擴展方法是帶有藍色下滑箭頭的
2、如果擴展方法和被擴展類中的方法相同,會怎么樣?
擴展方法的優先級總是比類型本身中定義的實例方法低,所以與接口或類方法具有相同名稱和簽名的擴展方法永遠不會被調用。
3、注意你是為哪個類型進行擴展,比如你是對string類型做擴展,還是對object類型、List<object>類型做擴展,一定要明確擴展的類型范圍。如果不確定擴展類應該放在哪個層次上,就放在最高層的類上~
4、是時候嘗試將你的那些Helper類庫轉換成擴展方法了
5、當你苦於類庫里沒有你想要的方法時,那就大膽的擴展吧
6、多擴展一些泛型類的擴展方法
7、擴展時仔細斟酌,最好能夠實現鏈式編程,多研究一下LinQ的源碼
8、對於擴展方法最好能夠放在一個專一存放各種擴展方法的命名空間里邊,並注意擴展方法和擴展類的命名格式
9、調用實例的擴展方法其實就是調用擴展類的靜態方法
總結:
擴展方法本身並不難,難點在於你是否能夠恰到好處地使用它,也就是說你是否知道在某個合適的時候使用它。