LINQ是我最喜歡的功能之一,程式中到處是data.Where(x=x>5).Select(x)等等的代碼,她使代碼看起來更好,更容易編寫,使用起來也超級方便,foreach使迴圈更加容易,而不用for int..,linq用起來那麼爽,那麼linq內部是如何實現的?我們如何自定義linq?我們這裡 ...
LINQ是我最喜歡的功能之一,程式中到處是data.Where(x=x>5).Select(x)等等的代碼,她使代碼看起來更好,更容易編寫,使用起來也超級方便,foreach使迴圈更加容易,而不用for int..,linq用起來那麼爽,那麼linq內部是如何實現的?我們如何自定義linq?我們這裡說的linq不是from score in scores where score > 80 select score;而是System.Linq哦。瞭解Ling之前先要瞭解擴展方法,因為linq的實質還是擴展方法。
擴展方法
擴展方法使你能夠向現有類型“添加”方法,而無需創建新的派生類型、重新編譯或以其他方式修改原始類型。 擴展方法是一種特殊的靜態方法,但可以像擴展類型上的實例方法一樣進行調用。
例如:
namespace ExtensionMethods { public static class MyExtensions { public static int WordCount(this string str) { return str.Split(new char[] { ' ', '.', '?' }, StringSplitOptions.RemoveEmptyEntries).Length; } } } //添加引用 using ExtensionMethods; //使用 string s = "Hello Extension Methods"; int i = s.WordCount();
微軟擴展方法建議
微軟MSDN上的建議:通常,建議只在不得已的情況下才實現擴展方法,並謹慎地實現。只要有可能,都應該通過創建從現有類型派生的新類型來達到這一目的。
擴展方法建議
1. 當功能與擴展類型最相關時,可以考慮使用擴展方法。
2. 當對第三方庫進行擴充的時候,可以考慮使用擴展方法。
3. 當您不希望將某些依賴項與擴展類型混合使用時,可以使用擴展方法來實現關註點分離。
4. 如果不確定到底使用還是不使用擴展方法,那就不要用。
擴展方法是C#語言的一個很好的補充,她使我們能夠編寫更好,更容易讀的代碼,但是也應該小心使用,不恰當的使用擴展方法可能導致可讀性降低,使測試困難,容易出錯。
System.Linq
System.Linq用起來那麼好,她內部是如何實現的,當然是查看源碼了。
Where源碼
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) { if (source == null) throw Error.ArgumentNull("source"); if (predicate == null) throw Error.ArgumentNull("predicate"); if (source is Iterator<TSource>) return ((Iterator<TSource>)source).Where(predicate); if (source is TSource[]) return new WhereArrayIterator<TSource>((TSource[])source, predicate); if (source is List<TSource>) return new WhereListIterator<TSource>((List<TSource>)source, predicate); return new WhereEnumerableIterator<TSource>(source, predicate); }
這個方法就是一個擴展方法,對數據進行了處理,具體的處理都是在對象中的MoveNext中
public override bool MoveNext() { if (state == 1) { while (index < source.Length) { TSource item = source[index]; index++; if (predicate(item)) { current = item; return true; } } Dispose(); } return false; }
可以看出就是一個迴圈處理,如果你覺得還是不清楚,可以看WhereIterator方法
static IEnumerable<TSource> WhereIterator<TSource>(IEnumerable<TSource> source, Func<TSource, int, bool> predicate) { int index = -1; foreach (TSource element in source) { checked { index++; } if (predicate(element, index)) yield return element; } }
這下明白了,linq就是擴展方法,對數據進行處理,返回所需要的數據,知道了原理之後,可以寫自己的linq擴展方法了。
我想寫一個帶有控制台輸出的Where擴展方法
public static IEnumerable<TSource> WhereWithLog<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) { if (source == null) { throw new ArgumentNullException("source", "不能為空"); } if (predicate == null) { throw new ArgumentNullException("predicate", "不能為空"); } int index = 0; foreach (var item in source) { Console.WriteLine($"Where item:{item},結果:{predicate(item)}"); if (predicate(item)) { yield return item; } index++; } }
實現一個打亂數據的擴展方法,這裡的方法用了約束,只能是值類型。
public static IEnumerable<T> ShuffleForStruct<T>(this IEnumerable<T> source) where T : struct { if (source == null) throw new ArgumentNullException("source", "不能為空"); var data = source.ToList(); int length = data.Count() - 1; for (int i = length; i > 0; i--) { int j = rd.Next(i + 1); var temp = data[j]; data[j] = data[i]; data[i] = temp; } return data; }
到此為止是不是覺得Enumerable中的方法也就是那麼回事,沒有那麼難,我也可以實現。
應評論的需要增加whereif,條件為true執行左邊的條件,false執行右邊的條件,例如:data.WhereIf(x => x > 5, x => x + 10, x => x - 10)
public static IEnumerable<TSource> WhereIf<TSource>(this IEnumerable<TSource> source, Func<TSource,bool> predicate, Func<TSource, TSource> truePredicate, Func<TSource, TSource> falsePredicate) { if (source == null) { throw new ArgumentNullException("source", "不能為空"); } if (predicate == null) { throw new ArgumentNullException("predicate", "不能為空"); } if (truePredicate == null) { throw new ArgumentNullException("truePredicate", "不能為空"); } if (falsePredicate == null) { throw new ArgumentNullException("falsePredicate", "不能為空"); } foreach (var item in source) { if (predicate(item)) { yield return truePredicate(item); } else { yield return falsePredicate(item); } } }
或者簡單的whereif,true執行條件,false不執行,例如:data.WhereIf(x => x > 5,true)
public static IEnumerable<TSource> WhereIf<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, bool condition) { return condition ? source.Where(predicate) : source; }