博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【开源】OSharp框架解说系列(3):扩展方法
阅读量:4948 次
发布时间:2019-06-11

本文共 33821 字,大约阅读时间需要 112 分钟。

OSharp是什么?

  OSharp是个快速开发框架,但不是一个大而全的包罗万象的框架,严格的说,OSharp中什么都没有实现。与其他大而全的框架最大的不同点,就是OSharp只做抽象封装,不做实现。依赖注入、ORM、对象映射、日志、缓存等等功能,都只定义了一套最基础最通用的抽象封装,提供了一套统一的API、约定与规则,并定义了部分执行流程,主要是让项目在一定的规范下进行开发。所有的功能实现端,都是通过现有的成熟的第三方组件来实现的,除了EntityFramework之外,所有的第三方实现都可以轻松的替换成另一种第三方实现,OSharp框架正是要起隔离作用,保证这种变更不会对业务代码造成影响,使用统一的API来进行业务实现,解除与第三方实现的耦合,保持业务代码的规范与稳定。

本文已同步到系列目录:

前言

扩展方法使你能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。 扩展方法是一种特殊的静态方法,但可以像扩展类型上的实例方法一样进行调用。 对于用 C# 和 Visual Basic 编写的客户端代码,调用扩展方法与调用在类型中实际定义的方法之间没有明显的差异。

最常见的扩展方法是 LINQ 标准查询运算符,它将查询功能添加到现有的  和  类型。 若要使用标准查询运算符,请先使用 using System.Linq 指令将它们置于范围中。 然后,任何实现了 的类型看起来都具有 、、 等实例方法。 在  类型的实例(如  或 )后键入“dot”时,可以在 IntelliSense 语句完成中看到这些附加方法。

  以上摘自MSDN的扩展方法描述,可见扩展方法是个很实用的语法糖,用过都说好。

  OSharp 框架中也定义(收集)了不少非常实用的扩展方法,位于工具组件OSharp.Utility.dll中,命名空间为OSharp.Utility.Extensions,下面我们来一一分解。

  注:本篇都是非常基础的代码,代码的功能都有注释,可讲的东西不多,思想性的东西就更少了,不想看代码的可以跳过本篇。

  声明:本文的扩展方法部分出自原创,部分收集整理于互联网(出处已不可考),冒犯之处望见谅。

  代码总览:

  

泛型扩展

  泛型扩展,如果没有类型限定,基本上就是 Object 类型的扩展。因为所有类型都继承自Object类型的,如果给Objext类型定义扩展方法,那么所有的类型都会增加这个方法,有时候反而会阻碍编码流畅性。

数据类型转换

  在编码的过程中,经常会遇到数据类型转换的需求,为了方便统一,我们可以定义一个通用的类型转换扩展方法。

1 ///  2 /// 把对象类型转换为指定类型 3 ///  4 ///  5 ///  6 /// 
7 public static object CastTo(this object value, Type conversionType) 8 { 9 if (value == null)10 {11 return null;12 }13 if (conversionType.IsNullableType())14 {15 conversionType = conversionType.GetUnNullableType();16 }17 if (conversionType.IsEnum)18 {19 return Enum.Parse(conversionType, value.ToString());20 }21 if (conversionType == typeof(Guid))22 {23 return Guid.Parse(value.ToString());24 }25 return Convert.ChangeType(value, conversionType);26 }27 28 /// 29 /// 把对象类型转化为指定类型30 /// 31 ///
动态类型
32 /// 要转化的源对象 33 ///
转化后的指定类型的对象,转化失败引发异常。
34 public static T CastTo
(this object value)35 {36 object result = CastTo(value, typeof(T));37 return (T)result;38 }39 40 ///
41 /// 把对象类型转化为指定类型,转化失败时返回指定的默认值42 /// 43 ///
动态类型
44 ///
要转化的源对象 45 ///
转化失败返回的指定默认值 46 ///
转化后的指定类型对象,转化失败时返回指定的默认值
47 public static T CastTo
(this object value, T defaultValue)48 {49 try50 {51 return CastTo
(value);52 }53 catch (Exception)54 {55 return defaultValue;56 }57 }

  使用示例:

1 Assert.AreEqual(((object)null).CastTo(), null); 2 Assert.AreEqual("123".CastTo
(), 123); 3 Assert.AreEqual(123.CastTo
(), "123"); 4 Assert.AreEqual(true.CastTo
(), "True"); 5 Assert.AreEqual("true".CastTo
(), true); 6 Assert.AreEqual("56D768A3-3D74-43B4-BD7B-2871D675CC4B".CastTo
(), new Guid("56D768A3-3D74-43B4-BD7B-2871D675CC4B")); 7 Assert.AreEqual(1.CastTo
(), UriKind.Absolute); 8 Assert.AreEqual("RelativeOrAbsolute".CastTo
(), UriKind.RelativeOrAbsolute); 9 Assert.AreEqual("abc".CastTo
(123), 123);10 ExceptionAssert.IsException
(() => "abc".CastTo
());

JSON序列化

  很多时候都要把对象序列化成JSON字符串,又不想让项目到处依赖于JSON.NET这个第三方类库,定义一个JSON序列化的扩展方法,让工具组件来引用JSON.NET做这件事吧

1 ///  2 /// 将对象序列化为JSON字符串,不支持存在循环引用的对象 3 ///  4 /// 
动态类型
5 /// 动态类型对象 6 ///
JSON字符串
7 public static string ToJsonString
(this T value) 8 { 9 return JsonConvert.SerializeObject(value);10 }

  有来有往,JSON反序列化:

1 ///  2 /// 将JSON字符串还原为对象 3 ///  4 /// 
要转换的目标类型
5 /// JSON字符串 6 ///
7 public static T FromJsonString
(this string json) 8 { 9 json.CheckNotNull("json");10 return JsonConvert.DeserializeObject
(json);11 }

MVC中的View直接使用 linq to entities 查询出来的匿名对象

  EntityFramework 使用 linq 查询匿名结果的方式很好用,性能又好,爽呆了,但由于匿名结果的可访问性是 internal,可是不能直接返回给View(View在编译之后是另外一个程序集了,internal不能跨程序集)使用,来个扩展方法专门把匿名对象转换为dynamic吧

1 ///  2 /// 将对象[主要是匿名对象]转换为dynamic 3 ///  4 public static dynamic ToDynamic(this object value) 5 { 6     IDictionary
expando = new ExpandoObject(); 7 Type type = value.GetType(); 8 PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(type); 9 foreach (PropertyDescriptor property in properties)10 {11 var val = property.GetValue(value);12 if (property.PropertyType.FullName.StartsWith("<>f__AnonymousType"))13 {14 dynamic dval = val.ToDynamic();15 expando.Add(property.Name, dval);16 }17 else18 {19 expando.Add(property.Name, val);20 }21 }22 return expando as ExpandoObject;23 }

  使用示例:

1 // Controller 端,转换为dynamic 2 var data = new { Id = 1, Name = "GMF" }; 3 dynamic result = data.ToDynamic(); 4 Viewbag.Result = result; 5 ... 6 //View 端,可以直接用了 7 dynamic result = Viewbag.Result; 8 @result.Id 9 @result.Name10 ...

字符串扩展

正则表达式

  使用正则表达式的原生静态方法,比较难用,做成扩展方法就好用多了

字符串操作

1 ///  2 /// 指示所指定的正则表达式在指定的输入字符串中是否找到了匹配项 3 ///  4 /// 要搜索匹配项的字符串 5 /// 要匹配的正则表达式模式 6 /// 
如果正则表达式找到匹配项,则为 true;否则,为 false
7 public static bool IsMatch(this string value, string pattern) 8 { 9 if (value == null)10 {11 return false;12 }13 return Regex.IsMatch(value, pattern);14 }15 16 /// 17 /// 在指定的输入字符串中搜索指定的正则表达式的第一个匹配项18 /// 19 /// 要搜索匹配项的字符串20 /// 要匹配的正则表达式模式21 ///
一个对象,包含有关匹配项的信息
22 public static string Match(this string value, string pattern)23 {24 if (value == null)25 {26 return null;27 }28 return Regex.Match(value, pattern).Value;29 }30 31 /// 32 /// 在指定的输入字符串中搜索指定的正则表达式的所有匹配项的字符串集合33 /// 34 /// 要搜索匹配项的字符串 35 /// 要匹配的正则表达式模式 36 ///
一个集合,包含有关匹配项的字符串值
37 public static IEnumerable
Matches(this string value, string pattern)38 {39 if (value == null)40 {41 return new string[] { };42 }43 MatchCollection matches = Regex.Matches(value, pattern);44 return from Match match in matches select match.Value;45 }

常用字符串正则判断

1 ///  2 /// 是否电子邮件 3 ///  4 public static bool IsEmail(this string value) 5 { 6     const string pattern = @"^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$"; 7     return value.IsMatch(pattern); 8 } 9 10 /// 11 /// 是否是IP地址12 /// 13 public static bool IsIpAddress(this string value)14 {15     const string pattern = @"^(\d(25[0-5]|2[0-4][0-9]|1?[0-9]?[0-9])\d\.){3}\d(25[0-5]|2[0-4][0-9]|1?[0-9]?[0-9])\d$";16     return value.IsMatch(pattern);17 }18 19 /// 20 /// 是否是整数21 /// 22 public static bool IsNumeric(this string value)23 {24     const string pattern = @"^\-?[0-9]+$";25     return value.IsMatch(pattern);26 }27 28 /// 29 /// 是否是Unicode字符串30 /// 31 public static bool IsUnicode(this string value)32 {33     const string pattern = @"^[\u4E00-\u9FA5\uE815-\uFA29]+$";34     return value.IsMatch(pattern);35 }36 37 /// 38 /// 是否Url字符串39 /// 40 public static bool IsUrl(this string value)41 {42     const string pattern = @"^(http|https|ftp|rtsp|mms):(\/\/|\\\\)[A-Za-z0-9%\-_@]+\.[A-Za-z0-9%\-_@]+[A-Za-z0-9\.\/=\?%\-&_~`@:\+!;]*$";43     return value.IsMatch(pattern);44 }45 46 /// 47 /// 是否身份证号,验证如下3种情况:48 /// 1.身份证号码为15位数字;49 /// 2.身份证号码为18位数字;50 /// 3.身份证号码为17位数字+1个字母51 /// 52 public static bool IsIdentityCard(this string value)53 {54     const string pattern = @"^(^\d{15}$|^\d{18}$|^\d{17}(\d|X|x))$";55     return value.IsMatch(pattern);56 }57 58 /// 59 /// 是否手机号码60 /// 61 /// 62 /// 是否按严格格式验证63 public static bool IsMobileNumber(this string value, bool isRestrict = false)64 {65     string pattern = isRestrict ? @"^[1][3-8]\d{9}$" : @"^[1]\d{10}$";66     return value.IsMatch(pattern);67 }

简化操作

  字符串的静态方法有时候用着实在别扭,包装成扩展方法,在编码的时候就感觉顺畅多了。

1 ///  2 /// 指示指定的字符串是 null 还是 System.String.Empty 字符串 3 ///  4 public static bool IsNullOrEmpty(this string value) 5 { 6     return string.IsNullOrEmpty(value); 7 } 8  9 /// 10 /// 指示指定的字符串是 null、空还是仅由空白字符组成。11 /// 12 public static bool IsNullOrWhiteSpace(this string value)13 {14     return string.IsNullOrWhiteSpace(value);15 }16 17 /// 18 /// 为指定格式的字符串填充相应对象来生成字符串19 /// 20 /// 字符串格式,占位符以{n}表示21 /// 用于填充占位符的参数22 /// 
格式化后的字符串
23 public static string FormatWith(this string format, params object[] args)24 {25 format.CheckNotNull("format");26 return string.Format(CultureInfo.CurrentCulture, format, args);27 }28 29 /// 30 /// 将字符串反转31 /// 32 /// 要反转的字符串33 public static string ReverseString(this string value)34 {35 value.CheckNotNull("value");36 return new string(value.Reverse().ToArray());37 }38 39 /// 40 /// 以指定字符串作为分隔符将指定字符串分隔成数组41 /// 42 /// 要分割的字符串43 /// 字符串类型的分隔符44 /// 是否移除数据中元素为空字符串的项45 ///
分割后的数据
46 public static string[] Split(this string value, string strSplit, bool removeEmptyEntries = false)47 {48 return value.Split(new[] { strSplit }, removeEmptyEntries ? StringSplitOptions.RemoveEmptyEntries : StringSplitOptions.None);49 }50 51 /// 52 /// 获取字符串的MD5 Hash值53 /// 54 public static string ToMd5Hash(this string value)55 {56 return HashHelper.GetMd5(value);57 }

类型扩展

   类型相关的代码,通常都是一些反射的代码,利用扩展方法,可以使操作有效的简化。

Nullable类型

1 ///  2 /// 判断类型是否为Nullable类型 3 ///  4 ///  要处理的类型  5 /// 
是返回True,不是返回False
6 public static bool IsNullableType(this Type type) 7 { 8 return ((type != null) && type.IsGenericType) && (type.GetGenericTypeDefinition() == typeof(Nullable<>)); 9 }10 11 /// 12 /// 通过类型转换器获取Nullable类型的基础类型13 /// 14 /// 要处理的类型对象 15 ///
16 public static Type GetUnNullableType(this Type type)17 {18 if (IsNullableType(type))19 {20 NullableConverter nullableConverter = new NullableConverter(type);21 return nullableConverter.UnderlyingType;22 }23 return type;24 }

   使用示例:

1 Assert.IsTrue(typeof(int?).IsNullableType());2 Assert.IsTrue(typeof(Nullable
).IsNullableType());3 Assert.IsFalse(typeof(int).IsNullableType());4 5 Assert.AreEqual(typeof(int?).GetUnNullableType(), typeof(int));6 Assert.AreEqual(typeof(Nullable
).GetUnNullableType(), typeof(int));7 Assert.AreEqual(typeof(int).GetUnNullableType(), typeof(int));

Attribute特性操作

1 ///  2 /// 获取成员元数据的Description特性描述信息 3 ///  4 /// 成员元数据对象 5 /// 是否搜索成员的继承链以查找描述特性 6 /// 
返回Description特性描述信息,如不存在则返回成员的名称
7 public static string ToDescription(this MemberInfo member, bool inherit = false) 8 { 9 DescriptionAttribute desc = member.GetAttribute
(inherit);10 return desc == null ? member.Name : desc.Description;11 }12 13 ///
14 /// 检查指定指定类型成员中是否存在指定的Attribute特性15 /// 16 ///
要检查的Attribute特性类型
17 ///
要检查的类型成员18 ///
是否从继承中查找19 ///
是否存在
20 public static bool AttributeExists
(this MemberInfo memberInfo, bool inherit = false) where T : Attribute21 {22 return memberInfo.GetCustomAttributes(typeof(T), inherit).Any(m => (m as T) != null);23 }24 25 ///
26 /// 从类型成员获取指定Attribute特性27 /// 28 ///
Attribute特性类型
29 ///
类型类型成员30 ///
是否从继承中查找31 ///
存在返回第一个,不存在返回null
32 public static T GetAttribute
(this MemberInfo memberInfo, bool inherit = false) where T : Attribute33 {34 var descripts = memberInfo.GetCustomAttributes(typeof(T), inherit);35 return descripts.FirstOrDefault() as T;36 }37 38 ///
39 /// 从类型成员获取指定Attribute特性40 /// 41 ///
Attribute特性类型
42 ///
类型类型成员43 ///
是否从继承中查找44 ///
返回所有指定Attribute特性的数组
45 public static T[] GetAttributes
(this MemberInfo memberInfo, bool inherit = false) where T : Attribute46 {47 return memberInfo.GetCustomAttributes(typeof(T), inherit).Cast
().ToArray();48 }

  使用示例:  

//测试类型[Description("测试实体")]public class TestEntity{    [Description("名称")]    public string Name { get; set; }}//使用示例Type type = typeof(TestEntity);Assert.AreEqual(type.ToDescription(), "测试实体");PropertyInfo property = type.GetProperty("Name");Assert.AreEqual(property.ToDescription(), "名称");type = typeof(string);Assert.AreEqual(type.ToDescription(), "String"); //没有指定特性,返回类型名称

 其他

1 ///  2 /// 判断类型是否为集合类型 3 ///  4 /// 要处理的类型 5 /// 
是返回True,不是返回False
6 public static bool IsEnumerable(this Type type) 7 { 8 if (type == typeof(string)) 9 {10 return false;11 }12 return typeof(IEnumerable).IsAssignableFrom(type);13 }14 15 /// 16 /// 判断当前泛型类型是否可由指定类型的实例填充17 /// 18 /// 泛型类型19 /// 指定类型20 ///
21 public static bool IsGenericAssignableFrom(this Type genericType, Type type)22 {23 genericType.CheckNotNull("genericType");24 type.CheckNotNull("type");25 if (!genericType.IsGenericType)26 {27 throw new ArgumentException("该功能只支持泛型类型的调用,非泛型类型可使用 IsAssignableFrom 方法。");28 }29 30 List
allOthers = new List
{ type };31 if (genericType.IsInterface)32 {33 allOthers.AddRange(type.GetInterfaces());34 }35 36 foreach (var other in allOthers)37 {38 Type cur = other;39 while (cur != null)40 {41 if (cur.IsGenericType)42 {43 cur = cur.GetGenericTypeDefinition();44 }45 if (cur.IsSubclassOf(genericType) || cur == genericType)46 {47 return true;48 }49 cur = cur.BaseType;50 }51 }52 return false;53 }

随机数扩展

   Random 随机数功能,也是非常常用的,.net fx 中已经定义了比如Next,NextDouble这些功能了,下面我们再定义一些其他的常用随机数操作,在编写代码的时候,更行云流水。

  注:Random 的随机性是伪随机的,最好使用全局存在的Random对象,才能保证随机性,如果是即时声明的Random对象,在短时间内可能失去随机性。

1 ///   2 /// 返回随机布尔值  3 ///   4 ///   5 /// 
随机布尔值
6 public static bool NextBoolean(this Random random) 7 { 8 return random.NextDouble() > 0.5; 9 } 10 11 /// 12 /// 返回指定枚举类型的随机枚举值 13 /// 14 /// 15 ///
指定枚举类型的随机枚举值
16 public static T NextEnum
(this Random random) where T : struct 17 { 18 Type type = typeof(T); 19 if (!type.IsEnum) 20 { 21 throw new InvalidOperationException(); 22 } 23 Array array = Enum.GetValues(type); 24 int index = random.Next(array.GetLowerBound(0), array.GetUpperBound(0) + 1); 25 return (T)array.GetValue(index); 26 } 27 28 ///
29 /// 返回随机数填充的指定长度的数组 30 /// 31 ///
32 ///
数组长度 33 ///
随机数填充的指定长度的数组
34 public static byte[] NextBytes(this Random random, int length) 35 { 36 if (length < 0) 37 { 38 throw new ArgumentOutOfRangeException("length"); 39 } 40 byte[] data = new byte[length]; 41 random.NextBytes(data); 42 return data; 43 } 44 45 ///
46 /// 返回数组中的随机元素 47 /// 48 ///
元素类型
49 ///
50 ///
元素数组 51 ///
元素数组中的某个随机项
52 public static T NextItem
(this Random random, T[] items) 53 { 54 return items[random.Next(0, items.Length)]; 55 } 56 57 ///
58 /// 返回指定时间段内的随机时间值 59 /// 60 ///
61 ///
时间范围的最小值 62 ///
时间范围的最大值 63 ///
指定时间段内的随机时间值
64 public static DateTime NextDateTime(this Random random, DateTime minValue, DateTime maxValue) 65 { 66 long ticks = minValue.Ticks + (long)((maxValue.Ticks - minValue.Ticks) * random.NextDouble()); 67 return new DateTime(ticks); 68 } 69 70 ///
71 /// 返回随机时间值 72 /// 73 ///
74 ///
随机时间值
75 public static DateTime NextDateTime(this Random random) 76 { 77 return NextDateTime(random, DateTime.MinValue, DateTime.MaxValue); 78 } 79 80 ///
81 /// 获取指定的长度的随机数字字符串 82 /// 83 ///
84 ///
要获取随机数长度 85 ///
指定长度的随机数字符串
86 public static string GetRandomNumberString(this Random random, int length) 87 { 88 if (length < 0) 89 { 90 throw new ArgumentOutOfRangeException("length"); 91 } 92 char[] pattern = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; 93 string result = ""; 94 int n = pattern.Length; 95 for (int i = 0; i < length; i++) 96 { 97 int rnd = random.Next(0, n); 98 result += pattern[rnd]; 99 }100 return result;101 }102 103 ///
104 /// 获取指定的长度的随机字母字符串105 /// 106 ///
107 ///
要获取随机数长度108 ///
指定长度的随机字母组成字符串
109 public static string GetRandomLetterString(this Random random, int length)110 {111 if (length < 0)112 {113 throw new ArgumentOutOfRangeException("length");114 }115 char[] pattern = new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',116 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };117 string result = "";118 int n = pattern.Length;119 for (int i = 0; i < length; i++)120 {121 int rnd = random.Next(0, n);122 result += pattern[rnd];123 }124 return result;125 }126 127 ///
128 /// 获取指定的长度的随机字母和数字字符串129 /// 130 ///
131 ///
要获取随机数长度132 ///
指定长度的随机字母和数字组成字符串
133 public static string GetRandomLetterAndNumberString(this Random random, int length)134 {135 if (length < 0)136 {137 throw new ArgumentOutOfRangeException("length");138 }139 char[] pattern = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 140 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',141 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };142 string result = "";143 int n = pattern.Length;144 for (int i = 0; i < length; i++)145 {146 int rnd = random.Next(0, n);147 result += pattern[rnd];148 }149 return result;150 }

参数检查扩展

   一个饱满健壮的软件,检查用户输入是必不少的步骤,永远也不要相信用户输入的数据,即早对输入的非法数据进行拦截,有利于及早发现错误,使非法数据的破坏减至最小。

  参数检查至关重要,那么,什么时候该进行参数检查呢,我个人认为:

  • 所有的公共方法都应该进行检查。因为是公共方法,你无法控制调用方会以怎样的方式来进行调用,所以,都应该进行参数检查。
  • 对于私有方法,在可控范围内,可以不进行参数检查。

  通常我们做参数检查,会使用如下的方式

1 public static void Method(string input, int number) 2 { 3     if (input == null) 4     { 5         throw new ArgumentNullException("input"); 6     } 7     if (number <= 0) 8     { 9         throw new ArgumentOutOfRangeException("number");10     }11     // do something12 }

  写法虽然不复杂,但整天写这样的代码,也会累的,而且一个简单就得占4行的空间,我们需要一种更简单的方式。为了适应各种异常的抛出,我们定义一个异常的泛型方法来执行最终的异常抛出。

1 ///  2 /// 验证指定值的断言
是否为真,如果不为真,抛出指定消息
的指定类型
异常 3 ///
4 ///
异常类型
5 /// 要验证的断言。 6 /// 异常消息。 7 private static void Require
(bool assertion, string message) where TException : Exception 8 { 9 if (assertion)10 {11 return;12 }13 TException exception = (TException)Activator.CreateInstance(typeof(TException), message);14 throw exception;15 }

  定义了一个bool参数assertion来接收是是否要抛出异常的判断结果,同时还需要抛出异常消息。看到上面这段代码,有人就坐不住了,这里使用的反射来实例化异常实例,会带来性能问题吗?大可不必,试想,都异常了,还需要在乎这个的性能问题吗?

  前面的ArgumentNullException异常,可以定义如下的扩展方法

1 ///  2 /// 检查参数不能为空引用,否则抛出
异常。 3 ///
4 /// 5 /// 参数名称 6 ///
7 public static void CheckNotNull
(this T value, string paramName) where T : class 8 { 9 Require
(value != null, string.Format(Resources.ParameterCheck_NotNull, paramName));10 }

  这样,要对 input 参数进行检查,就相当的简单了: input.CheckNotNull("input");

 引用类型的参数检查

  类似的,对于引用类型,我们可以定义出如下扩展方法:

1 ///  2 /// 检查字符串不能为空引用或空字符串,否则抛出
异常或
异常。 3 ///
4 /// 5 /// 参数名称。 6 ///
7 ///
8 public static void CheckNotNullOrEmpty(this string value, string paramName) 9 {10 value.CheckNotNull(paramName);11 Require
(value.Length > 0, string.Format(Resources.ParameterCheck_NotNullOrEmpty_String, paramName));12 }13 14 ///
15 /// 检查Guid值不能为Guid.Empty,否则抛出
异常。16 ///
17 ///
18 ///
参数名称。19 ///
20 public static void CheckNotEmpty(this Guid value, string paramName)21 {22 Require
(value != Guid.Empty, string.Format(Resources.ParameterCheck_NotEmpty_Guid, paramName));23 }24 25 ///
26 /// 检查集合不能为空引用或空集合,否则抛出
异常或
异常。27 ///
28 ///
集合项的类型。
29 ///
30 ///
参数名称。31 ///
32 ///
33 public static void CheckNotNullOrEmpty
(this IEnumerable
collection, string paramName)34 {35 collection.CheckNotNull(paramName);36 Require
(collection.Any(), string.Format(Resources.ParameterCheck_NotNullOrEmpty_Collection, paramName));37 }

值类型的参数检查

  对于值类型,可以定义出如下扩展方法:

1 ///  2 /// 检查参数必须小于[或可等于,参数canEqual]指定值,否则抛出
异常。 3 ///
4 ///
参数类型。
5 /// 6 /// 参数名称。 7 /// 要比较的值。 8 /// 是否可等于。 9 ///
10 public static void CheckLessThan
(this T value, string paramName, T target, bool canEqual = false) where T : IComparable
11 {12 bool flag = canEqual ? value.CompareTo(target) <= 0 : value.CompareTo(target) < 0;13 string format = canEqual ? Resources.ParameterCheck_NotLessThanOrEqual : Resources.ParameterCheck_NotLessThan;14 Require
(flag, string.Format(format, paramName, target));15 }16 17 ///
18 /// 检查参数必须大于[或可等于,参数canEqual]指定值,否则抛出
异常。19 ///
20 ///
参数类型。
21 ///
22 ///
参数名称。23 ///
要比较的值。24 ///
是否可等于。25 ///
26 public static void CheckGreaterThan
(this T value, string paramName, T target, bool canEqual = false) where T : IComparable
27 {28 bool flag = canEqual ? value.CompareTo(target) >= 0 : value.CompareTo(target) > 0;29 string format = canEqual ? Resources.ParameterCheck_NotGreaterThanOrEqual : Resources.ParameterCheck_NotGreaterThan;30 Require
(flag, string.Format(format, paramName, target));31 }32 33 ///
34 /// 检查参数必须在指定范围之间,否则抛出
异常。35 ///
36 ///
参数类型。
37 ///
38 ///
参数名称。39 ///
比较范围的起始值。40 ///
比较范围的结束值。41 ///
是否可等于起始值42 ///
是否可等于结束值43 ///
44 public static void CheckBetween
(this T value, string paramName, T start, T end, bool startEqual = false, bool endEqual = false)45 where T : IComparable
46 {47 bool flag = startEqual ? value.CompareTo(start) >= 0 : value.CompareTo(start) > 0;48 string message = startEqual49 ? string.Format(Resources.ParameterCheck_BetweenNotEqual, paramName, start, end, start)50 : string.Format(Resources.ParameterCheck_Between, paramName, start, end);51 Require
(flag, message);52 53 flag = endEqual ? value.CompareTo(end) <= 0 : value.CompareTo(end) < 0;54 message = endEqual55 ? string.Format(Resources.ParameterCheck_BetweenNotEqual, paramName, start, end, end)56 : string.Format(Resources.ParameterCheck_Between, paramName, start, end);57 Require
(flag, message);58 }

 文件相关检查

  常用的参数检查还有文件夹与文件是否存在的检查:

1 ///  2 /// 检查指定路径的文件夹必须存在,否则抛出
异常。 3 ///
4 /// 5 /// 参数名称。 6 ///
7 ///
8 public static void CheckDirectoryExists(this string directory, string paramName = null) 9 {10 CheckNotNull(directory, paramName);11 Require
(Directory.Exists(directory), string.Format(Resources.ParameterCheck_DirectoryNotExists, directory));12 }13 14 ///
15 /// 检查指定路径的文件必须存在,否则抛出
异常。16 ///
17 ///
18 ///
参数名称。19 ///
当文件路径为null时
20 ///
当文件路径不存在时
21 public static void CheckFileExists(this string filename, string paramName = null)22 {23 CheckNotNull(filename, paramName);24 Require
(File.Exists(filename), string.Format(Resources.ParameterCheck_FileNotExists, filename));25 }

其他参数检查

  当然,还可能会有其他的情况,抛出其他类型的异常,定义如下两个通用的扩展:

1 ///  2 /// 验证指定值的断言表达式是否为真,不为值抛出
异常 3 ///
4 /// 5 /// 要验证的断言表达式 6 /// 异常消息 7 public static void Required
(this T value, Func
assertionFunc, string message) 8 { 9 Require
(assertionFunc(value), message);10 }11 12 ///
13 /// 验证指定值的断言表达式是否为真,不为真抛出
异常14 ///
15 ///
要判断的值的类型
16 ///
抛出的异常类型
17 ///
要判断的值18 ///
要验证的断言表达式19 ///
异常消息20 public static void Required
(this T value, Func
assertionFunc, string message) where TException : Exception21 {22 Require
(assertionFunc(value), message);23 }

Expression表达式扩展

  在使用Expression进行linq查询时,有时候需要对表达式进行 And,Or 等组合,可定义如下扩展方法来简化操作

1 ///  2 /// 以特定的条件运行组合两个Expression表达式 3 ///  4 /// 
表达式的主实体类型
5 /// 第一个Expression表达式 6 /// 要组合的Expression表达式 7 /// 组合条件运算方式 8 ///
组合后的表达式
9 public static Expression
Compose
(this Expression
first, Expression
second, Func
merge)10 {11 Dictionary
map =12 first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f);13 Expression secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);14 return Expression.Lambda
(merge(first.Body, secondBody), first.Parameters);15 }16 17 ///
18 /// 以 Expression.AndAlso 组合两个Expression表达式19 /// 20 ///
表达式的主实体类型
21 ///
第一个Expression表达式22 ///
要组合的Expression表达式23 ///
组合后的表达式
24 public static Expression
> And
(this Expression
> first, Expression
> second)25 {26 return first.Compose(second, Expression.AndAlso);27 }28 29 ///
30 /// 以 Expression.OrElse 组合两个Expression表达式31 /// 32 ///
表达式的主实体类型
33 ///
第一个Expression表达式34 ///
要组合的Expression表达式35 ///
组合后的表达式
36 public static Expression
> Or
(this Expression
> first, Expression
> second)37 {38 return first.Compose(second, Expression.OrElse);39 }40 41 42 private class ParameterRebinder : ExpressionVisitor43 {44 private readonly Dictionary
_map;45 46 private ParameterRebinder(Dictionary
map)47 {48 _map = map ?? new Dictionary
();49 }50 51 public static Expression ReplaceParameters(Dictionary
map, Expression exp)52 {53 return new ParameterRebinder(map).Visit(exp);54 }55 56 protected override Expression VisitParameter(ParameterExpression node)57 {58 ParameterExpression replacement;59 if (_map.TryGetValue(node, out replacement))60 {61 node = replacement;62 }63 return base.VisitParameter(node);64 }65 }

使用示例:

1 // And 2 Expression
> predicate = m => m.IsDeleted; 3 Expression
> actual = m => m.IsDeleted && m.Id > 500; 4 predicate = predicate.And(m => m.Id > 500); 5 List
list1 = Entities.Where(predicate.Compile()).ToList(); 6 List
list2 = Entities.Where(actual.Compile()).ToList(); 7 Assert.IsTrue(list1.SequenceEqual(list2)); 8 9 //Or10 Expression
> predicate = m => m.IsDeleted;11 Expression
> actual = m => m.IsDeleted || m.Id > 500;12 predicate = predicate.Or(m => m.Id > 500);13 List
list1 = Entities.Where(predicate.Compile()).ToList();14 List
list2 = Entities.Where(actual.Compile()).ToList();15 Assert.IsTrue(list1.SequenceEqual(list2));

集合操作扩展

  集合操作扩展,主要是对IEnumerable<T>与IQueryable<T>两个集合进行扩展

1 ///   2     /// 集合扩展方法类  3     ///   4     public static class CollectionExtensions  5     {  6         #region IEnumerable的扩展  7   8         ///   9         /// 将集合展开并分别转换成字符串,再以指定的分隔符衔接,拼成一个字符串返回。默认分隔符为逗号 10         ///  11         ///  要处理的集合  12         ///  分隔符,默认为逗号  13         /// 
拼接后的字符串
14 public static string ExpandAndToString
(this IEnumerable
collection, string separator = ",") 15 { 16 return collection.ExpandAndToString(t => t.ToString(), separator); 17 } 18 19 ///
20 /// 循环集合的每一项,调用委托生成字符串,返回合并后的字符串。默认分隔符为逗号 21 /// 22 ///
待处理的集合 23 ///
单个集合项的转换委托 24 ///
分隔符,默认为逗号 25 ///
泛型类型
26 ///
27 public static string ExpandAndToString
(this IEnumerable
collection, Func
itemFormatFunc, string separetor = ",") 28 { 29 collection = collection as IList
?? collection.ToList(); 30 itemFormatFunc.CheckNotNull("itemFormatFunc"); 31 if (!collection.Any()) 32 { 33 return null; 34 } 35 StringBuilder sb = new StringBuilder(); 36 int i = 0; 37 int count = collection.Count(); 38 foreach (T t in collection) 39 { 40 if (i == count - 1) 41 { 42 sb.Append(itemFormatFunc(t)); 43 } 44 else 45 { 46 sb.Append(itemFormatFunc(t) + separetor); 47 } 48 i++; 49 } 50 return sb.ToString(); 51 } 52 53 ///
54 /// 集合是否为空 55 /// 56 ///
要处理的集合 57 ///
动态类型
58 ///
为空返回True,不为空返回False
59 public static bool IsEmpty
(this IEnumerable
collection) 60 { 61 collection = collection as IList
?? collection.ToList(); 62 return !collection.Any(); 63 } 64 65 ///
66 /// 根据第三方条件是否为真来决定是否执行指定条件的查询 67 /// 68 ///
要查询的源 69 ///
查询条件 70 ///
第三方条件 71 ///
动态类型
72 ///
查询的结果
73 public static IEnumerable
WhereIf
(this IEnumerable
source, Func
predicate, bool condition) 74 { 75 predicate.CheckNotNull("predicate"); 76 source = source as IList
?? source.ToList(); 77 78 return condition ? source.Where(predicate) : source; 79 } 80 81 ///
82 /// 根据指定条件返回集合中不重复的元素 83 /// 84 ///
动态类型
85 ///
动态筛选条件类型
86 ///
要操作的源 87 ///
重复数据筛选条件 88 ///
不重复元素的集合
89 public static IEnumerable
DistinctBy
(this IEnumerable
source, Func
keySelector) 90 { 91 keySelector.CheckNotNull("keySelector"); 92 source = source as IList
?? source.ToList(); 93 94 return source.GroupBy(keySelector).Select(group => group.First()); 95 } 96 97 #endregion 98 99 #region IQueryable的扩展100 101 ///
102 /// 根据第三方条件是否为真来决定是否执行指定条件的查询103 /// 104 ///
要查询的源 105 ///
查询条件 106 ///
第三方条件 107 ///
动态类型
108 ///
查询的结果
109 public static IQueryable
WhereIf
(this IQueryable
source, Expression
> predicate, bool condition)110 {111 source.CheckNotNull("source");112 predicate.CheckNotNull("predicate");113 114 return condition ? source.Where(predicate) : source;115 }116 117 ///
118 /// 把
集合按指定字段与排序方式进行排序119 ///
120 ///
要排序的数据集121 ///
排序属性名122 ///
排序方向123 ///
动态类型
124 ///
排序后的数据集
125 public static IOrderedQueryable
OrderBy
(this IQueryable
source,126 string propertyName,127 ListSortDirection sortDirection = ListSortDirection.Ascending)128 {129 source.CheckNotNull("source");130 propertyName.CheckNotNullOrEmpty("propertyName");131 132 return QueryablePropertySorter
.OrderBy(source, propertyName, sortDirection);133 }134 135 ///
136 /// 把
集合按指定字段排序条件进行排序137 ///
138 ///
动态类型
139 ///
要排序的数据集140 ///
列表字段排序条件141 ///
142 public static IOrderedQueryable
OrderBy
(this IQueryable
source, SortCondition sortCondition)143 {144 source.CheckNotNull("source");145 sortCondition.CheckNotNull("sortCondition");146 147 return source.OrderBy(sortCondition.SortField, sortCondition.ListSortDirection);148 }149 150 ///
151 /// 把
集合继续按指定字段排序方式进行排序152 ///
153 ///
动态类型
154 ///
要排序的数据集155 ///
排序属性名156 ///
排序方向157 ///
158 public static IOrderedQueryable
ThenBy
(this IOrderedQueryable
source,159 string propertyName,160 ListSortDirection sortDirection = ListSortDirection.Ascending)161 {162 source.CheckNotNull("source");163 propertyName.CheckNotNullOrEmpty("propertyName");164 165 return QueryablePropertySorter
.ThenBy(source, propertyName, sortDirection);166 }167 168 ///
169 /// 把
集合继续指定字段排序方式进行排序170 ///
171 ///
动态类型
172 ///
要排序的数据集173 ///
列表字段排序条件174 ///
175 public static IOrderedQueryable
ThenBy
(this IOrderedQueryable
source, SortCondition sortCondition)176 {177 source.CheckNotNull("source");178 sortCondition.CheckNotNull("sortCondition");179 180 return source.ThenBy(sortCondition.SortField, sortCondition.ListSortDirection);181 }182 183 #endregion184 }

   使用示例:

1 //ExpandAndToString 2 List
source = new List
(); 3 //当为空集合时,返回null 4 Assert.AreEqual(source.ExpandAndToString(), null); 5 source.AddRange(new List
() { 1, 2, 3, 4, 5, 6 }); 6 //没有分隔符时,默认为逗号 7 Assert.AreEqual(source.ExpandAndToString(), "1,2,3,4,5,6"); 8 Assert.AreEqual(source.ExpandAndToString(null), "123456"); 9 Assert.AreEqual(source.ExpandAndToString(""), "123456");10 Assert.AreEqual(source.ExpandAndToString("|"), "1|2|3|4|5|6");11 12 List
source = new List
{ 1, 2, 3, 4, 5, 6 };13 14 //转换委托不能为空15 ExceptionAssert.IsException
(() => source.ExpandAndToString(null));16 //没有分隔符时,默认为逗号17 Assert.AreEqual(source.ExpandAndToString(item => (item + 1).ToString()), "2,3,4,5,6,7");18 Assert.AreEqual(source.ExpandAndToString(item => (item + 1).ToString(), "|"), "2|3|4|5|6|7");19 20 List
source = new List
();21 Assert.IsTrue(source.IsEmpty());22 23 source.Add(1);24 Assert.IsFalse(source.IsEmpty());25 26 27 List
source = new List
{ 1, 2, 3, 4, 5, 6, 7 };28 CollectionAssert.AreEqual(source.WhereIf(m => m > 5, false).ToList(), source);29 List
actual = new List
{ 6, 7 };30 CollectionAssert.AreEqual(source.WhereIf(m => m > 5, true).ToList(), actual);31 32 33 List
source = new List
{ 1, 2, 3, 3, 4, 4, 5, 6, 7, 7 };34 List
actual = new List
{ 1, 2, 3, 4, 5, 6, 7 };35 CollectionAssert.AreEqual(source.DistinctBy(m => m).ToList(), actual);

开源说明

github.com

   OSharp项目已在github.com上开源,地址为:,欢迎阅读代码,欢迎 Fork,如果您认同 OSharp 项目的思想,欢迎参与 OSharp 项目的开发。

  在Visual Studio 2013中,可直接获取 OSharp 的最新源代码,获取方式如下,地址为:https://github.com/i66soft/osharp.git

  

nuget

  OSharp的相关类库已经发布到nuget上,欢迎试用,直接在nuget上搜索 “osharp” 关键字即可找到

  

系列导航

本文已同步到系列目录:

转载于:https://www.cnblogs.com/guomingfeng/p/osharp-extensions.html

你可能感兴趣的文章
JavaScript(6)——事件1.0
查看>>
2013 ACM-ICPC China Nanjing Invitational Programming Contest 总结
查看>>
【Hibernate学习笔记-5】@Formula注解的使用
查看>>
链接元素<a>
查看>>
Binding object to winForm controller through VS2010 Designer(通过VS2010设计器将对象绑定到winForm控件上)...
查看>>
Spring Boot实战笔记(二)-- Spring常用配置(Scope、Spring EL和资源调用)
查看>>
前端性能优化集【持续更新】
查看>>
第二章:webdriver 控制浏览器窗口大小
查看>>
四则运算2初步构思
查看>>
Break the Chocolate(规律)
查看>>
C#jbox小节
查看>>
结构体指针释放的问题
查看>>
C#枚举Enum[轉]
查看>>
第三百五十七天 how can I 坚持
查看>>
【动态规划】流水作业调度问题与Johnson法则
查看>>
startActivityForResult不起作用
查看>>
Python&Selenium&Unittest&BeautifuReport 自动化测试并生成HTML自动化测试报告
查看>>
活现被翻转生命
查看>>
POJ 1228
查看>>
SwaggerUI+SpringMVC——构建RestFul API的可视化界面
查看>>