C#秘密武器之扩展方法
为何要用扩展方法?
作为一个.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、调用实例的扩展方法其实就是调用扩展类的静态方法
总结:
扩展方法本身并不难,难点在于你是否能够恰到好处地使用它,也就是说你是否知道在某个合适的时候使用它。