LINQ定義了一系列的標準查詢操作符,我們可以通過這些操作符使用查詢語法或者方法語法對數據源進行查詢,LINQ在定義查詢語句後並不會立即查詢數據源,而是通過foreach對返回結果進行遍歷的時候才會查詢數據源,這種技術即LINQ延遲查詢,舉例如下: 此段代碼輸出如下 由此可見,程式在執行foreac ...
LINQ定義了一系列的標準查詢操作符,我們可以通過這些操作符使用查詢語法或者方法語法對數據源進行查詢,LINQ在定義查詢語句後並不會立即查詢數據源,而是通過foreach對返回結果進行遍歷的時候才會查詢數據源,這種技術即LINQ延遲查詢,舉例如下:
//延遲查詢 int[] numbers = new int[] { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; int i = 0; var q = numbers.Where(x => { i++; return x > 2; }); foreach (var v in q) { Console.WriteLine("v = {0}, i = {1}", v, i); }
此段代碼輸出如下
由此可見,程式在執行foreach迴圈的時候才進行查詢,此時對程式稍作修改,將查詢後的結果轉換為List:
//延遲查詢 int[] numbers = new int[] { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; int i = 0; var q = numbers.Where(x => { i++; return x > 2; }).ToList(); foreach (var v in q) { Console.WriteLine("v = {0}, i = {1}", v, i); }
執行結果
程式在執行ToList的時候已經進行了查詢,所以返回i值全部為10。LINQ的延遲查詢為何如此,讓我們查看一下擴展方法Where的源碼,該方法在System.Linq命名空間下的Enumerable靜態類中(http://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,577032c8811e20d3):
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); }
也就是說定義var q = numbers.Where(x => { i++; return x > 2; });程式只是返回了一個WhereArrayIterator<TSource>對象,該對象有一個指向源數據和lambda表達式的引用,該類源碼如下:
class WhereArrayIterator<TSource> : Iterator<TSource> { TSource[] source; Func<TSource, bool> predicate; int index; public WhereArrayIterator(TSource[] source, Func<TSource, bool> predicate) { this.source = source; this.predicate = predicate; } public override Iterator<TSource> Clone() { return new WhereArrayIterator<TSource>(source, predicate); } 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; } public override IEnumerable<TResult> Select<TResult>(Func<TSource, TResult> selector) { return new WhereSelectArrayIterator<TSource, TResult>(source, predicate, selector); } public override IEnumerable<TSource> Where(Func<TSource, bool> predicate) { return new WhereArrayIterator<TSource>(source, CombinePredicates(this.predicate, predicate)); } }
我們看到MoveNext()方法下的if (predicate(item))語句,程式在此處對源數據的每一個值進行判定,所以當對返回的WhereArrayIterator<TSource>對象進行迴圈時,編譯器隱式調用它的MoveNext方法,才會執行查詢。
我們查看Enumerable類下的ToList擴展方法:
public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source) { if (source == null) throw Error.ArgumentNull("source"); return new List<TSource>(source); }
ToList方法返回一個新的List<TSource>對象,我們再看List<TSource>的構造函數
public List(IEnumerable<T> collection) { if (collection==null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection); Contract.EndContractBlock(); ICollection<T> c = collection as ICollection<T>; if( c != null) { int count = c.Count; if (count == 0) { _items = _emptyArray; } else { _items = new T[count]; c.CopyTo(_items, 0); _size = count; } } else { _size = 0; _items = _emptyArray; // This enumerable could be empty. Let Add allocate a new array, if needed. // Note it will also go to _defaultCapacity first, not 1, then 2, etc. using(IEnumerator<T> en = collection.GetEnumerator()) { while(en.MoveNext()) { Add(en.Current); } } } }
構造函數將元數據轉換為ICollection,因為我們的元數據類型無法轉換為ICollection,則返回null,之後執行else段的代碼,在此處執行en.MoveNext()進行查詢數據源。