OSharp是什么?
OSharp是個快速開發框架,但不是一個大而全的包羅萬象的框架,嚴格的說,OSharp中什么都沒有實現。與其他大而全的框架最大的不同點,就是OSharp只做抽象封裝,不做實現。依賴注入、ORM、對象映射、日志、緩存等等功能,都只定義了一套最基礎最通用的抽象封裝,提供了一套統一的API、約定與規則,並定義了部分執行流程,主要是讓項目在一定的規范下進行開發。所有的功能實現端,都是通過現有的成熟的第三方組件來實現的,除了EntityFramework之外,所有的第三方實現都可以輕松的替換成另一種第三方實現,OSharp框架正是要起隔離作用,保證這種變更不會對業務代碼造成影響,使用統一的API來進行業務實現,解除與第三方實現的耦合,保持業務代碼的規范與穩定。
本文已同步到系列目錄:OSharp快速開發框架解說系列
前言
擴展方法使你能夠向現有類型“添加”方法,而無需創建新的派生類型、重新編譯或以其他方式修改原始類型。 擴展方法是一種特殊的靜態方法,但可以像擴展類型上的實例方法一樣進行調用。 對於用 C# 和 Visual Basic 編寫的客戶端代碼,調用擴展方法與調用在類型中實際定義的方法之間沒有明顯的差異。
最常見的擴展方法是 LINQ 標准查詢運算符,它將查詢功能添加到現有的 System.Collections.IEnumerable 和 System.Collections.Generic.IEnumerable<T> 類型。 若要使用標准查詢運算符,請先使用 using System.Linq 指令將它們置於范圍中。 然后,任何實現了IEnumerable<T> 的類型看起來都具有 GroupBy、OrderBy、Average 等實例方法。 在 IEnumerable<T> 類型的實例(如 List<T> 或 Array)后鍵入“dot”時,可以在 IntelliSense 語句完成中看到這些附加方法。
以上摘自MSDN的擴展方法描述,可見擴展方法是個很實用的語法糖,用過都說好。
OSharp 框架中也定義(收集)了不少非常實用的擴展方法,位於工具組件OSharp.Utility.dll中,命名空間為OSharp.Utility.Extensions,下面我們來一一分解。
注:本篇都是非常基礎的代碼,代碼的功能都有注釋,可講的東西不多,思想性的東西就更少了,不想看代碼的可以跳過本篇。
聲明:本文的擴展方法部分出自原創,部分收集整理於互聯網(出處已不可考),冒犯之處望見諒。
代碼總覽:
泛型擴展
泛型擴展,如果沒有類型限定,基本上就是 Object 類型的擴展。因為所有類型都繼承自Object類型的,如果給Objext類型定義擴展方法,那么所有的類型都會增加這個方法,有時候反而會阻礙編碼流暢性。
數據類型轉換
在編碼的過程中,經常會遇到數據類型轉換的需求,為了方便統一,我們可以定義一個通用的類型轉換擴展方法。
1 /// <summary> 2 /// 把對象類型轉換為指定類型 3 /// </summary> 4 /// <param name="value"></param> 5 /// <param name="conversionType"></param> 6 /// <returns></returns> 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 /// <summary> 29 /// 把對象類型轉化為指定類型 30 /// </summary> 31 /// <typeparam name="T"> 動態類型 </typeparam> 32 /// <param name="value"> 要轉化的源對象 </param> 33 /// <returns> 轉化后的指定類型的對象,轉化失敗引發異常。 </returns> 34 public static T CastTo<T>(this object value) 35 { 36 object result = CastTo(value, typeof(T)); 37 return (T)result; 38 } 39 40 /// <summary> 41 /// 把對象類型轉化為指定類型,轉化失敗時返回指定的默認值 42 /// </summary> 43 /// <typeparam name="T"> 動態類型 </typeparam> 44 /// <param name="value"> 要轉化的源對象 </param> 45 /// <param name="defaultValue"> 轉化失敗返回的指定默認值 </param> 46 /// <returns> 轉化后的指定類型對象,轉化失敗時返回指定的默認值 </returns> 47 public static T CastTo<T>(this object value, T defaultValue) 48 { 49 try 50 { 51 return CastTo<T>(value); 52 } 53 catch (Exception) 54 { 55 return defaultValue; 56 } 57 }
使用示例:
1 Assert.AreEqual(((object)null).CastTo<object>(), null); 2 Assert.AreEqual("123".CastTo<int>(), 123); 3 Assert.AreEqual(123.CastTo<string>(), "123"); 4 Assert.AreEqual(true.CastTo<string>(), "True"); 5 Assert.AreEqual("true".CastTo<bool>(), true); 6 Assert.AreEqual("56D768A3-3D74-43B4-BD7B-2871D675CC4B".CastTo<Guid>(), new Guid("56D768A3-3D74-43B4-BD7B-2871D675CC4B")); 7 Assert.AreEqual(1.CastTo<UriKind>(), UriKind.Absolute); 8 Assert.AreEqual("RelativeOrAbsolute".CastTo<UriKind>(), UriKind.RelativeOrAbsolute); 9 Assert.AreEqual("abc".CastTo<int>(123), 123); 10 ExceptionAssert.IsException<FormatException>(() => "abc".CastTo<int>());
JSON序列化
很多時候都要把對象序列化成JSON字符串,又不想讓項目到處依賴於JSON.NET這個第三方類庫,定義一個JSON序列化的擴展方法,讓工具組件來引用JSON.NET做這件事吧
1 /// <summary> 2 /// 將對象序列化為JSON字符串,不支持存在循環引用的對象 3 /// </summary> 4 /// <typeparam name="T">動態類型</typeparam> 5 /// <param name="value">動態類型對象</param> 6 /// <returns>JSON字符串</returns> 7 public static string ToJsonString<T>(this T value) 8 { 9 return JsonConvert.SerializeObject(value); 10 }
有來有往,JSON反序列化:
1 /// <summary> 2 /// 將JSON字符串還原為對象 3 /// </summary> 4 /// <typeparam name="T">要轉換的目標類型</typeparam> 5 /// <param name="json">JSON字符串 </param> 6 /// <returns></returns> 7 public static T FromJsonString<T>(this string json) 8 { 9 json.CheckNotNull("json"); 10 return JsonConvert.DeserializeObject<T>(json); 11 }
MVC中的View直接使用 linq to entities 查詢出來的匿名對象
EntityFramework 使用 linq 查詢匿名結果的方式很好用,性能又好,爽呆了,但由於匿名結果的可訪問性是 internal,可是不能直接返回給View(View在編譯之后是另外一個程序集了,internal不能跨程序集)使用,來個擴展方法專門把匿名對象轉換為dynamic吧
1 /// <summary> 2 /// 將對象[主要是匿名對象]轉換為dynamic 3 /// </summary> 4 public static dynamic ToDynamic(this object value) 5 { 6 IDictionary<string, object> 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 else 18 { 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.Name 10 ...
字符串擴展
正則表達式
使用正則表達式的原生靜態方法,比較難用,做成擴展方法就好用多了
字符串操作
1 /// <summary> 2 /// 指示所指定的正則表達式在指定的輸入字符串中是否找到了匹配項 3 /// </summary> 4 /// <param name="value">要搜索匹配項的字符串</param> 5 /// <param name="pattern">要匹配的正則表達式模式</param> 6 /// <returns>如果正則表達式找到匹配項,則為 true;否則,為 false</returns> 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 /// <summary> 17 /// 在指定的輸入字符串中搜索指定的正則表達式的第一個匹配項 18 /// </summary> 19 /// <param name="value">要搜索匹配項的字符串</param> 20 /// <param name="pattern">要匹配的正則表達式模式</param> 21 /// <returns>一個對象,包含有關匹配項的信息</returns> 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 /// <summary> 32 /// 在指定的輸入字符串中搜索指定的正則表達式的所有匹配項的字符串集合 33 /// </summary> 34 /// <param name="value"> 要搜索匹配項的字符串 </param> 35 /// <param name="pattern"> 要匹配的正則表達式模式 </param> 36 /// <returns> 一個集合,包含有關匹配項的字符串值 </returns> 37 public static IEnumerable<string> 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 /// <summary> 2 /// 是否電子郵件 3 /// </summary> 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 /// <summary> 11 /// 是否是IP地址 12 /// </summary> 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 /// <summary> 20 /// 是否是整數 21 /// </summary> 22 public static bool IsNumeric(this string value) 23 { 24 const string pattern = @"^\-?[0-9]+$"; 25 return value.IsMatch(pattern); 26 } 27 28 /// <summary> 29 /// 是否是Unicode字符串 30 /// </summary> 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 /// <summary> 38 /// 是否Url字符串 39 /// </summary> 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 /// <summary> 47 /// 是否身份證號,驗證如下3種情況: 48 /// 1.身份證號碼為15位數字; 49 /// 2.身份證號碼為18位數字; 50 /// 3.身份證號碼為17位數字+1個字母 51 /// </summary> 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 /// <summary> 59 /// 是否手機號碼 60 /// </summary> 61 /// <param name="value"></param> 62 /// <param name="isRestrict">是否按嚴格格式驗證</param> 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 /// <summary> 2 /// 指示指定的字符串是 null 還是 System.String.Empty 字符串 3 /// </summary> 4 public static bool IsNullOrEmpty(this string value) 5 { 6 return string.IsNullOrEmpty(value); 7 } 8 9 /// <summary> 10 /// 指示指定的字符串是 null、空還是僅由空白字符組成。 11 /// </summary> 12 public static bool IsNullOrWhiteSpace(this string value) 13 { 14 return string.IsNullOrWhiteSpace(value); 15 } 16 17 /// <summary> 18 /// 為指定格式的字符串填充相應對象來生成字符串 19 /// </summary> 20 /// <param name="format">字符串格式,占位符以{n}表示</param> 21 /// <param name="args">用於填充占位符的參數</param> 22 /// <returns>格式化后的字符串</returns> 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 /// <summary> 30 /// 將字符串反轉 31 /// </summary> 32 /// <param name="value">要反轉的字符串</param> 33 public static string ReverseString(this string value) 34 { 35 value.CheckNotNull("value"); 36 return new string(value.Reverse().ToArray()); 37 } 38 39 /// <summary> 40 /// 以指定字符串作為分隔符將指定字符串分隔成數組 41 /// </summary> 42 /// <param name="value">要分割的字符串</param> 43 /// <param name="strSplit">字符串類型的分隔符</param> 44 /// <param name="removeEmptyEntries">是否移除數據中元素為空字符串的項</param> 45 /// <returns>分割后的數據</returns> 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 /// <summary> 52 /// 獲取字符串的MD5 Hash值 53 /// </summary> 54 public static string ToMd5Hash(this string value) 55 { 56 return HashHelper.GetMd5(value); 57 }
類型擴展
類型相關的代碼,通常都是一些反射的代碼,利用擴展方法,可以使操作有效的簡化。
Nullable類型
1 /// <summary> 2 /// 判斷類型是否為Nullable類型 3 /// </summary> 4 /// <param name="type"> 要處理的類型 </param> 5 /// <returns> 是返回True,不是返回False </returns> 6 public static bool IsNullableType(this Type type) 7 { 8 return ((type != null) && type.IsGenericType) && (type.GetGenericTypeDefinition() == typeof(Nullable<>)); 9 } 10 11 /// <summary> 12 /// 通過類型轉換器獲取Nullable類型的基礎類型 13 /// </summary> 14 /// <param name="type"> 要處理的類型對象 </param> 15 /// <returns> </returns> 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<int>).IsNullableType()); 3 Assert.IsFalse(typeof(int).IsNullableType()); 4 5 Assert.AreEqual(typeof(int?).GetUnNullableType(), typeof(int)); 6 Assert.AreEqual(typeof(Nullable<int>).GetUnNullableType(), typeof(int)); 7 Assert.AreEqual(typeof(int).GetUnNullableType(), typeof(int));
Attribute特性操作
1 /// <summary> 2 /// 獲取成員元數據的Description特性描述信息 3 /// </summary> 4 /// <param name="member">成員元數據對象</param> 5 /// <param name="inherit">是否搜索成員的繼承鏈以查找描述特性</param> 6 /// <returns>返回Description特性描述信息,如不存在則返回成員的名稱</returns> 7 public static string ToDescription(this MemberInfo member, bool inherit = false) 8 { 9 DescriptionAttribute desc = member.GetAttribute<DescriptionAttribute>(inherit); 10 return desc == null ? member.Name : desc.Description; 11 } 12 13 /// <summary> 14 /// 檢查指定指定類型成員中是否存在指定的Attribute特性 15 /// </summary> 16 /// <typeparam name="T">要檢查的Attribute特性類型</typeparam> 17 /// <param name="memberInfo">要檢查的類型成員</param> 18 /// <param name="inherit">是否從繼承中查找</param> 19 /// <returns>是否存在</returns> 20 public static bool AttributeExists<T>(this MemberInfo memberInfo, bool inherit = false) where T : Attribute 21 { 22 return memberInfo.GetCustomAttributes(typeof(T), inherit).Any(m => (m as T) != null); 23 } 24 25 /// <summary> 26 /// 從類型成員獲取指定Attribute特性 27 /// </summary> 28 /// <typeparam name="T">Attribute特性類型</typeparam> 29 /// <param name="memberInfo">類型類型成員</param> 30 /// <param name="inherit">是否從繼承中查找</param> 31 /// <returns>存在返回第一個,不存在返回null</returns> 32 public static T GetAttribute<T>(this MemberInfo memberInfo, bool inherit = false) where T : Attribute 33 { 34 var descripts = memberInfo.GetCustomAttributes(typeof(T), inherit); 35 return descripts.FirstOrDefault() as T; 36 } 37 38 /// <summary> 39 /// 從類型成員獲取指定Attribute特性 40 /// </summary> 41 /// <typeparam name="T">Attribute特性類型</typeparam> 42 /// <param name="memberInfo">類型類型成員</param> 43 /// <param name="inherit">是否從繼承中查找</param> 44 /// <returns>返回所有指定Attribute特性的數組</returns> 45 public static T[] GetAttributes<T>(this MemberInfo memberInfo, bool inherit = false) where T : Attribute 46 { 47 return memberInfo.GetCustomAttributes(typeof(T), inherit).Cast<T>().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 /// <summary> 2 /// 判斷類型是否為集合類型 3 /// </summary> 4 /// <param name="type">要處理的類型</param> 5 /// <returns>是返回True,不是返回False</returns> 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 /// <summary> 16 /// 判斷當前泛型類型是否可由指定類型的實例填充 17 /// </summary> 18 /// <param name="genericType">泛型類型</param> 19 /// <param name="type">指定類型</param> 20 /// <returns></returns> 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<Type> allOthers = new List<Type> { 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 /// <summary> 2 /// 返回隨機布爾值 3 /// </summary> 4 /// <param name="random"></param> 5 /// <returns>隨機布爾值</returns> 6 public static bool NextBoolean(this Random random) 7 { 8 return random.NextDouble() > 0.5; 9 } 10 11 /// <summary> 12 /// 返回指定枚舉類型的隨機枚舉值 13 /// </summary> 14 /// <param name="random"></param> 15 /// <returns>指定枚舉類型的隨機枚舉值</returns> 16 public static T NextEnum<T>(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 /// <summary> 29 /// 返回隨機數填充的指定長度的數組 30 /// </summary> 31 /// <param name="random"></param> 32 /// <param name="length">數組長度</param> 33 /// <returns>隨機數填充的指定長度的數組</returns> 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 /// <summary> 46 /// 返回數組中的隨機元素 47 /// </summary> 48 /// <typeparam name="T">元素類型</typeparam> 49 /// <param name="random"></param> 50 /// <param name="items">元素數組</param> 51 /// <returns>元素數組中的某個隨機項</returns> 52 public static T NextItem<T>(this Random random, T[] items) 53 { 54 return items[random.Next(0, items.Length)]; 55 } 56 57 /// <summary> 58 /// 返回指定時間段內的隨機時間值 59 /// </summary> 60 /// <param name="random"></param> 61 /// <param name="minValue">時間范圍的最小值</param> 62 /// <param name="maxValue">時間范圍的最大值</param> 63 /// <returns>指定時間段內的隨機時間值</returns> 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 /// <summary> 71 /// 返回隨機時間值 72 /// </summary> 73 /// <param name="random"></param> 74 /// <returns>隨機時間值</returns> 75 public static DateTime NextDateTime(this Random random) 76 { 77 return NextDateTime(random, DateTime.MinValue, DateTime.MaxValue); 78 } 79 80 /// <summary> 81 /// 獲取指定的長度的隨機數字字符串 82 /// </summary> 83 /// <param name="random"></param> 84 /// <param name="length">要獲取隨機數長度</param> 85 /// <returns>指定長度的隨機數字符串</returns> 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 /// <summary> 104 /// 獲取指定的長度的隨機字母字符串 105 /// </summary> 106 /// <param name="random"></param> 107 /// <param name="length">要獲取隨機數長度</param> 108 /// <returns>指定長度的隨機字母組成字符串</returns> 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 /// <summary> 128 /// 獲取指定的長度的隨機字母和數字字符串 129 /// </summary> 130 /// <param name="random"></param> 131 /// <param name="length">要獲取隨機數長度</param> 132 /// <returns>指定長度的隨機字母和數字組成字符串</returns> 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 something 12 }
寫法雖然不復雜,但整天寫這樣的代碼,也會累的,而且一個簡單就得占4行的空間,我們需要一種更簡單的方式。為了適應各種異常的拋出,我們定義一個異常的泛型方法來執行最終的異常拋出。
1 /// <summary> 2 /// 驗證指定值的斷言<paramref name="assertion"/>是否為真,如果不為真,拋出指定消息<paramref name="message"/>的指定類型<typeparamref name="TException"/>異常 3 /// </summary> 4 /// <typeparam name="TException">異常類型</typeparam> 5 /// <param name="assertion">要驗證的斷言。</param> 6 /// <param name="message">異常消息。</param> 7 private static void Require<TException>(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 /// <summary> 2 /// 檢查參數不能為空引用,否則拋出<see cref="ArgumentNullException"/>異常。 3 /// </summary> 4 /// <param name="value"></param> 5 /// <param name="paramName">參數名稱</param> 6 /// <exception cref="ArgumentNullException"></exception> 7 public static void CheckNotNull<T>(this T value, string paramName) where T : class 8 { 9 Require<ArgumentNullException>(value != null, string.Format(Resources.ParameterCheck_NotNull, paramName)); 10 }
這樣,要對 input 參數進行檢查,就相當的簡單了: input.CheckNotNull("input");
引用類型的參數檢查
類似的,對於引用類型,我們可以定義出如下擴展方法:
1 /// <summary> 2 /// 檢查字符串不能為空引用或空字符串,否則拋出<see cref="ArgumentNullException"/>異常或<see cref="ArgumentException"/>異常。 3 /// </summary> 4 /// <param name="value"></param> 5 /// <param name="paramName">參數名稱。</param> 6 /// <exception cref="ArgumentNullException"></exception> 7 /// <exception cref="ArgumentException"></exception> 8 public static void CheckNotNullOrEmpty(this string value, string paramName) 9 { 10 value.CheckNotNull(paramName); 11 Require<ArgumentException>(value.Length > 0, string.Format(Resources.ParameterCheck_NotNullOrEmpty_String, paramName)); 12 } 13 14 /// <summary> 15 /// 檢查Guid值不能為Guid.Empty,否則拋出<see cref="ArgumentException"/>異常。 16 /// </summary> 17 /// <param name="value"></param> 18 /// <param name="paramName">參數名稱。</param> 19 /// <exception cref="ArgumentException"></exception> 20 public static void CheckNotEmpty(this Guid value, string paramName) 21 { 22 Require<ArgumentException>(value != Guid.Empty, string.Format(Resources.ParameterCheck_NotEmpty_Guid, paramName)); 23 } 24 25 /// <summary> 26 /// 檢查集合不能為空引用或空集合,否則拋出<see cref="ArgumentNullException"/>異常或<see cref="ArgumentException"/>異常。 27 /// </summary> 28 /// <typeparam name="T">集合項的類型。</typeparam> 29 /// <param name="collection"></param> 30 /// <param name="paramName">參數名稱。</param> 31 /// <exception cref="ArgumentNullException"></exception> 32 /// <exception cref="ArgumentException"></exception> 33 public static void CheckNotNullOrEmpty<T>(this IEnumerable<T> collection, string paramName) 34 { 35 collection.CheckNotNull(paramName); 36 Require<ArgumentException>(collection.Any(), string.Format(Resources.ParameterCheck_NotNullOrEmpty_Collection, paramName)); 37 }
值類型的參數檢查
對於值類型,可以定義出如下擴展方法:
1 /// <summary> 2 /// 檢查參數必須小於[或可等於,參數canEqual]指定值,否則拋出<see cref="ArgumentOutOfRangeException"/>異常。 3 /// </summary> 4 /// <typeparam name="T">參數類型。</typeparam> 5 /// <param name="value"></param> 6 /// <param name="paramName">參數名稱。</param> 7 /// <param name="target">要比較的值。</param> 8 /// <param name="canEqual">是否可等於。</param> 9 /// <exception cref="ArgumentOutOfRangeException"></exception> 10 public static void CheckLessThan<T>(this T value, string paramName, T target, bool canEqual = false) where T : IComparable<T> 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<ArgumentOutOfRangeException>(flag, string.Format(format, paramName, target)); 15 } 16 17 /// <summary> 18 /// 檢查參數必須大於[或可等於,參數canEqual]指定值,否則拋出<see cref="ArgumentOutOfRangeException"/>異常。 19 /// </summary> 20 /// <typeparam name="T">參數類型。</typeparam> 21 /// <param name="value"></param> 22 /// <param name="paramName">參數名稱。</param> 23 /// <param name="target">要比較的值。</param> 24 /// <param name="canEqual">是否可等於。</param> 25 /// <exception cref="ArgumentOutOfRangeException"></exception> 26 public static void CheckGreaterThan<T>(this T value, string paramName, T target, bool canEqual = false) where T : IComparable<T> 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<ArgumentOutOfRangeException>(flag, string.Format(format, paramName, target)); 31 } 32 33 /// <summary> 34 /// 檢查參數必須在指定范圍之間,否則拋出<see cref="ArgumentOutOfRangeException"/>異常。 35 /// </summary> 36 /// <typeparam name="T">參數類型。</typeparam> 37 /// <param name="value"></param> 38 /// <param name="paramName">參數名稱。</param> 39 /// <param name="start">比較范圍的起始值。</param> 40 /// <param name="end">比較范圍的結束值。</param> 41 /// <param name="startEqual">是否可等於起始值</param> 42 /// <param name="endEqual">是否可等於結束值</param> 43 /// <exception cref="ArgumentOutOfRangeException"></exception> 44 public static void CheckBetween<T>(this T value, string paramName, T start, T end, bool startEqual = false, bool endEqual = false) 45 where T : IComparable<T> 46 { 47 bool flag = startEqual ? value.CompareTo(start) >= 0 : value.CompareTo(start) > 0; 48 string message = startEqual 49 ? string.Format(Resources.ParameterCheck_BetweenNotEqual, paramName, start, end, start) 50 : string.Format(Resources.ParameterCheck_Between, paramName, start, end); 51 Require<ArgumentOutOfRangeException>(flag, message); 52 53 flag = endEqual ? value.CompareTo(end) <= 0 : value.CompareTo(end) < 0; 54 message = endEqual 55 ? string.Format(Resources.ParameterCheck_BetweenNotEqual, paramName, start, end, end) 56 : string.Format(Resources.ParameterCheck_Between, paramName, start, end); 57 Require<ArgumentOutOfRangeException>(flag, message); 58 }
文件相關檢查
常用的參數檢查還有文件夾與文件是否存在的檢查:
1 /// <summary> 2 /// 檢查指定路徑的文件夾必須存在,否則拋出<see cref="DirectoryNotFoundException"/>異常。 3 /// </summary> 4 /// <param name="directory"></param> 5 /// <param name="paramName">參數名稱。</param> 6 /// <exception cref="ArgumentNullException"></exception> 7 /// <exception cref="DirectoryNotFoundException"></exception> 8 public static void CheckDirectoryExists(this string directory, string paramName = null) 9 { 10 CheckNotNull(directory, paramName); 11 Require<DirectoryNotFoundException>(Directory.Exists(directory), string.Format(Resources.ParameterCheck_DirectoryNotExists, directory)); 12 } 13 14 /// <summary> 15 /// 檢查指定路徑的文件必須存在,否則拋出<see cref="FileNotFoundException"/>異常。 16 /// </summary> 17 /// <param name="filename"></param> 18 /// <param name="paramName">參數名稱。</param> 19 /// <exception cref="ArgumentNullException">當文件路徑為null時</exception> 20 /// <exception cref="FileNotFoundException">當文件路徑不存在時</exception> 21 public static void CheckFileExists(this string filename, string paramName = null) 22 { 23 CheckNotNull(filename, paramName); 24 Require<FileNotFoundException>(File.Exists(filename), string.Format(Resources.ParameterCheck_FileNotExists, filename)); 25 }
其他參數檢查
當然,還可能會有其他的情況,拋出其他類型的異常,定義如下兩個通用的擴展:
1 /// <summary> 2 /// 驗證指定值的斷言表達式是否為真,不為值拋出<see cref="Exception"/>異常 3 /// </summary> 4 /// <param name="value"></param> 5 /// <param name="assertionFunc">要驗證的斷言表達式</param> 6 /// <param name="message">異常消息</param> 7 public static void Required<T>(this T value, Func<T, bool> assertionFunc, string message) 8 { 9 Require<Exception>(assertionFunc(value), message); 10 } 11 12 /// <summary> 13 /// 驗證指定值的斷言表達式是否為真,不為真拋出<typeparam name="TException>"/>異常 14 /// </summary> 15 /// <typeparam name="T">要判斷的值的類型</typeparam> 16 /// <typeparam name="TException">拋出的異常類型</typeparam> 17 /// <param name="value">要判斷的值</param> 18 /// <param name="assertionFunc">要驗證的斷言表達式</param> 19 /// <param name="message">異常消息</param> 20 public static void Required<T, TException>(this T value, Func<T, bool> assertionFunc, string message) where TException : Exception 21 { 22 Require<TException>(assertionFunc(value), message); 23 }
Expression表達式擴展
在使用Expression進行linq查詢時,有時候需要對表達式進行 And,Or 等組合,可定義如下擴展方法來簡化操作
1 /// <summary> 2 /// 以特定的條件運行組合兩個Expression表達式 3 /// </summary> 4 /// <typeparam name="T">表達式的主實體類型</typeparam> 5 /// <param name="first">第一個Expression表達式</param> 6 /// <param name="second">要組合的Expression表達式</param> 7 /// <param name="merge">組合條件運算方式</param> 8 /// <returns>組合后的表達式</returns> 9 public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge) 10 { 11 Dictionary<ParameterExpression, ParameterExpression> 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<T>(merge(first.Body, secondBody), first.Parameters); 15 } 16 17 /// <summary> 18 /// 以 Expression.AndAlso 組合兩個Expression表達式 19 /// </summary> 20 /// <typeparam name="T">表達式的主實體類型</typeparam> 21 /// <param name="first">第一個Expression表達式</param> 22 /// <param name="second">要組合的Expression表達式</param> 23 /// <returns>組合后的表達式</returns> 24 public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) 25 { 26 return first.Compose(second, Expression.AndAlso); 27 } 28 29 /// <summary> 30 /// 以 Expression.OrElse 組合兩個Expression表達式 31 /// </summary> 32 /// <typeparam name="T">表達式的主實體類型</typeparam> 33 /// <param name="first">第一個Expression表達式</param> 34 /// <param name="second">要組合的Expression表達式</param> 35 /// <returns>組合后的表達式</returns> 36 public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) 37 { 38 return first.Compose(second, Expression.OrElse); 39 } 40 41 42 private class ParameterRebinder : ExpressionVisitor 43 { 44 private readonly Dictionary<ParameterExpression, ParameterExpression> _map; 45 46 private ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map) 47 { 48 _map = map ?? new Dictionary<ParameterExpression, ParameterExpression>(); 49 } 50 51 public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> 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<Func<TestEntity, bool>> predicate = m => m.IsDeleted; 3 Expression<Func<TestEntity, bool>> actual = m => m.IsDeleted && m.Id > 500; 4 predicate = predicate.And(m => m.Id > 500); 5 List<TestEntity> list1 = Entities.Where(predicate.Compile()).ToList(); 6 List<TestEntity> list2 = Entities.Where(actual.Compile()).ToList(); 7 Assert.IsTrue(list1.SequenceEqual(list2)); 8 9 //Or 10 Expression<Func<TestEntity, bool>> predicate = m => m.IsDeleted; 11 Expression<Func<TestEntity, bool>> actual = m => m.IsDeleted || m.Id > 500; 12 predicate = predicate.Or(m => m.Id > 500); 13 List<TestEntity> list1 = Entities.Where(predicate.Compile()).ToList(); 14 List<TestEntity> list2 = Entities.Where(actual.Compile()).ToList(); 15 Assert.IsTrue(list1.SequenceEqual(list2));
集合操作擴展
集合操作擴展,主要是對IEnumerable<T>與IQueryable<T>兩個集合進行擴展
1 /// <summary> 2 /// 集合擴展方法類 3 /// </summary> 4 public static class CollectionExtensions 5 { 6 #region IEnumerable的擴展 7 8 /// <summary> 9 /// 將集合展開並分別轉換成字符串,再以指定的分隔符銜接,拼成一個字符串返回。默認分隔符為逗號 10 /// </summary> 11 /// <param name="collection"> 要處理的集合 </param> 12 /// <param name="separator"> 分隔符,默認為逗號 </param> 13 /// <returns> 拼接后的字符串 </returns> 14 public static string ExpandAndToString<T>(this IEnumerable<T> collection, string separator = ",") 15 { 16 return collection.ExpandAndToString(t => t.ToString(), separator); 17 } 18 19 /// <summary> 20 /// 循環集合的每一項,調用委托生成字符串,返回合並后的字符串。默認分隔符為逗號 21 /// </summary> 22 /// <param name="collection">待處理的集合</param> 23 /// <param name="itemFormatFunc">單個集合項的轉換委托</param> 24 /// <param name="separetor">分隔符,默認為逗號</param> 25 /// <typeparam name="T">泛型類型</typeparam> 26 /// <returns></returns> 27 public static string ExpandAndToString<T>(this IEnumerable<T> collection, Func<T, string> itemFormatFunc, string separetor = ",") 28 { 29 collection = collection as IList<T> ?? 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 /// <summary> 54 /// 集合是否為空 55 /// </summary> 56 /// <param name="collection"> 要處理的集合 </param> 57 /// <typeparam name="T"> 動態類型 </typeparam> 58 /// <returns> 為空返回True,不為空返回False </returns> 59 public static bool IsEmpty<T>(this IEnumerable<T> collection) 60 { 61 collection = collection as IList<T> ?? collection.ToList(); 62 return !collection.Any(); 63 } 64 65 /// <summary> 66 /// 根據第三方條件是否為真來決定是否執行指定條件的查詢 67 /// </summary> 68 /// <param name="source"> 要查詢的源 </param> 69 /// <param name="predicate"> 查詢條件 </param> 70 /// <param name="condition"> 第三方條件 </param> 71 /// <typeparam name="T"> 動態類型 </typeparam> 72 /// <returns> 查詢的結果 </returns> 73 public static IEnumerable<T> WhereIf<T>(this IEnumerable<T> source, Func<T, bool> predicate, bool condition) 74 { 75 predicate.CheckNotNull("predicate"); 76 source = source as IList<T> ?? source.ToList(); 77 78 return condition ? source.Where(predicate) : source; 79 } 80 81 /// <summary> 82 /// 根據指定條件返回集合中不重復的元素 83 /// </summary> 84 /// <typeparam name="T">動態類型</typeparam> 85 /// <typeparam name="TKey">動態篩選條件類型</typeparam> 86 /// <param name="source">要操作的源</param> 87 /// <param name="keySelector">重復數據篩選條件</param> 88 /// <returns>不重復元素的集合</returns> 89 public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector) 90 { 91 keySelector.CheckNotNull("keySelector"); 92 source = source as IList<T> ?? source.ToList(); 93 94 return source.GroupBy(keySelector).Select(group => group.First()); 95 } 96 97 #endregion 98 99 #region IQueryable的擴展 100 101 /// <summary> 102 /// 根據第三方條件是否為真來決定是否執行指定條件的查詢 103 /// </summary> 104 /// <param name="source"> 要查詢的源 </param> 105 /// <param name="predicate"> 查詢條件 </param> 106 /// <param name="condition"> 第三方條件 </param> 107 /// <typeparam name="T"> 動態類型 </typeparam> 108 /// <returns> 查詢的結果 </returns> 109 public static IQueryable<T> WhereIf<T>(this IQueryable<T> source, Expression<Func<T, bool>> predicate, bool condition) 110 { 111 source.CheckNotNull("source"); 112 predicate.CheckNotNull("predicate"); 113 114 return condition ? source.Where(predicate) : source; 115 } 116 117 /// <summary> 118 /// 把<see cref="IQueryable{T}"/>集合按指定字段與排序方式進行排序 119 /// </summary> 120 /// <param name="source">要排序的數據集</param> 121 /// <param name="propertyName">排序屬性名</param> 122 /// <param name="sortDirection">排序方向</param> 123 /// <typeparam name="T">動態類型</typeparam> 124 /// <returns>排序后的數據集</returns> 125 public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, 126 string propertyName, 127 ListSortDirection sortDirection = ListSortDirection.Ascending) 128 { 129 source.CheckNotNull("source"); 130 propertyName.CheckNotNullOrEmpty("propertyName"); 131 132 return QueryablePropertySorter<T>.OrderBy(source, propertyName, sortDirection); 133 } 134 135 /// <summary> 136 /// 把<see cref="IQueryable{T}"/>集合按指定字段排序條件進行排序 137 /// </summary> 138 /// <typeparam name="T">動態類型</typeparam> 139 /// <param name="source">要排序的數據集</param> 140 /// <param name="sortCondition">列表字段排序條件</param> 141 /// <returns></returns> 142 public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, SortCondition sortCondition) 143 { 144 source.CheckNotNull("source"); 145 sortCondition.CheckNotNull("sortCondition"); 146 147 return source.OrderBy(sortCondition.SortField, sortCondition.ListSortDirection); 148 } 149 150 /// <summary> 151 /// 把<see cref="IOrderedQueryable{T}"/>集合繼續按指定字段排序方式進行排序 152 /// </summary> 153 /// <typeparam name="T">動態類型</typeparam> 154 /// <param name="source">要排序的數據集</param> 155 /// <param name="propertyName">排序屬性名</param> 156 /// <param name="sortDirection">排序方向</param> 157 /// <returns></returns> 158 public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, 159 string propertyName, 160 ListSortDirection sortDirection = ListSortDirection.Ascending) 161 { 162 source.CheckNotNull("source"); 163 propertyName.CheckNotNullOrEmpty("propertyName"); 164 165 return QueryablePropertySorter<T>.ThenBy(source, propertyName, sortDirection); 166 } 167 168 /// <summary> 169 /// 把<see cref="IOrderedQueryable{T}"/>集合繼續指定字段排序方式進行排序 170 /// </summary> 171 /// <typeparam name="T">動態類型</typeparam> 172 /// <param name="source">要排序的數據集</param> 173 /// <param name="sortCondition">列表字段排序條件</param> 174 /// <returns></returns> 175 public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, SortCondition sortCondition) 176 { 177 source.CheckNotNull("source"); 178 sortCondition.CheckNotNull("sortCondition"); 179 180 return source.ThenBy(sortCondition.SortField, sortCondition.ListSortDirection); 181 } 182 183 #endregion 184 }
使用示例:
1 //ExpandAndToString 2 List<int> source = new List<int>(); 3 //當為空集合時,返回null 4 Assert.AreEqual(source.ExpandAndToString(), null); 5 source.AddRange(new List<int>() { 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<int> source = new List<int> { 1, 2, 3, 4, 5, 6 }; 13 14 //轉換委托不能為空 15 ExceptionAssert.IsException<ArgumentNullException>(() => 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<int> source = new List<int>(); 21 Assert.IsTrue(source.IsEmpty()); 22 23 source.Add(1); 24 Assert.IsFalse(source.IsEmpty()); 25 26 27 List<int> source = new List<int> { 1, 2, 3, 4, 5, 6, 7 }; 28 CollectionAssert.AreEqual(source.WhereIf(m => m > 5, false).ToList(), source); 29 List<int> actual = new List<int> { 6, 7 }; 30 CollectionAssert.AreEqual(source.WhereIf(m => m > 5, true).ToList(), actual); 31 32 33 List<int> source = new List<int> { 1, 2, 3, 3, 4, 4, 5, 6, 7, 7 }; 34 List<int> actual = new List<int> { 1, 2, 3, 4, 5, 6, 7 }; 35 CollectionAssert.AreEqual(source.DistinctBy(m => m).ToList(), actual);
開源說明
github.com
OSharp項目已在github.com上開源,地址為:https://github.com/i66soft/osharp,歡迎閱讀代碼,歡迎 Fork,如果您認同 OSharp 項目的思想,歡迎參與 OSharp 項目的開發。
在Visual Studio 2013中,可直接獲取 OSharp 的最新源代碼,獲取方式如下,地址為:https://github.com/i66soft/osharp.git
nuget
OSharp的相關類庫已經發布到nuget上,歡迎試用,直接在nuget上搜索 “osharp” 關鍵字即可找到
系列導航
本文已同步到系列目錄:OSharp快速開發框架解說系列