擴展方法 之 基本數據篇
前一篇我列舉了幾個最常用到的基于Asp.Net的擴展方法,而這一篇基于基本數據的擴展方法理應不會遜一籌,因為它不局限于Asp.Net。何謂基本數據,這里直接擺定義:
C# 中有兩種基本數據類型:值類型和引用類型。 值類型包括:簡單類型、結構類型、枚舉類型;引用類型包括:Object 類型、類類型、接口、代表元、字符串類型、數組。
說白了這篇就是擴展 int, string, double, DateTime...等基本類型。這么多數據類型,如果int來個擴展,double也來個擴展,肯定會是一個造金字塔工程。幸好有泛型幫我們,但是有時泛型也不是萬能的,這個以后有機會再說。
為什么我們需要擴展方法?一句話:提高寫代碼的速度,語義也清晰。按道理說,基于基本數據的擴展方法應該經常被用到才有意義,否則只會“污染”被擴展的元素。而怎么才算常用,這可沒什么標準,也不是我一個人說了算。所以,以下的擴展方法大家就根據自己的經驗判斷是否常用吧,至少我的項目是用了不少。
1. In 判斷一個元素是否在一個集合里面
(這是我目前為止見到最好的擴展方法之一)
1.1 ScottGu 最初的版本:
1.2 后經 鶴沖天 的潤色:c#擴展方法奇思妙用高級篇一:改進 Scottgu 的 "In" 擴展
1.3 最后我也來湊個熱鬧:
public static bool In(this T t, params T[] c) { return c.Contains(t); //return c.Any(i => i.Equals(t)); }
示例:
bool exist1= 2.In(1, 2, 3); string[] helloworld = { "Hello", "World", "!" }; bool exist2 = "Hello".In(helloworld );
2. InRange 判斷一個元素是否在某個范圍
public static bool InRange(this IComparable t, T minT, T maxT) { return t.CompareTo(minT) >= 0 && t.CompareTo(maxT) <= 0; } public static bool InRange(this IComparable t, object minT, object maxT) { return t.CompareTo(minT) >= 0 && t.CompareTo(maxT) <= 0; }
示例:
//判斷3是否在2~3的范圍 bool result1 = 3.InRange(2, 3); //判斷3.14是否在3.13~3.15的范圍 bool result2 = 3.14.InRange(3.13, 3.15); //判斷今天是否在2000年1月1日~2010年1月1日的范圍 bool result3 = DateTime.Now.InRange(new DateTime(2000, 1, 1), new DateTime(2010, 1, 1)); //判斷牛B是否在牛A和牛C之間 bool result4 = "牛B".InRange("牛A", "牛C");
上面最后一個示例來源于這句網語:做一個徘徊于牛A和牛C之間的人
3. ForEach 遍歷集合
public static void ForEach(this IEnumerable source, Action action) { foreach (T element in source) action(element); } public static void ForEach(this IEnumerable source, Actionint> action) { int i = 0; foreach (T element in source) action(element, i++); }
注:這兩個并非原創,但實在太通用,已經找不到出處。
示例1:Linq to SQL 批量更新
//Linq to SQL IQueryable query = ...; query.ForEach(c => c.Name = "Bruce"); db.SubmitChanges();
示例2:設置行號
List list = ...; list.ForEach((c,i) => c.Line = (i+1));
4. Clone 克隆一個對象
public static T Clone(this T t) { return (T)CloneObject(t); } private static object CloneObject(object obj) { using (MemoryStream memStream = new MemoryStream()) { BinaryFormatter binaryFormatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone)); binaryFormatter.Serialize(memStream, obj); memStream.Seek(0, SeekOrigin.Begin); return binaryFormatter.Deserialize(memStream); } }
示例1:克隆單個實體
Entity entity = new Entity { Name = "Bruce", Line = 1 }; Entity cloneEntity = entity.Clone(); bool same = entity.Equals(cloneEntity); // 被克隆的類必須標記為可序列化 [Serializable] class Entity { public string Name { get; set; } public int Line { get; set; } }
最后entity.Equals(cloneEntity);的結果是false,表明已經成功克隆了一個新對象。
示例2:克隆集合
List list = new List(); list.Add(new Entity { Name = "Bruce", Line = 1 }); list.Add(new Entity { Name = "Jack", Line = 2 }); list.Add(new Entity { Name = "Rose", Line = 3 }); list.Add(new Entity { Name = "Tony", Line = 4 }); List cloneList = list.Clone();
總結:
擴展方法為我們封裝了常用的邏輯,與以往靜態方法不同的是,從使用者的角度來看已經不用關心這個方法是來自于那個類,主要記著命名空間就可以。所以,一般做法是,所有擴展方法屬于同一命名空間,并按被擴展的類型分類,如下圖:
最后要說的是,其實string類型的擴展方法應該是使用頻率最高的,但基于string的擴展方法網上隨便一抓一大把,這里就不重復列舉了。