今天工作的時候 發生個小BUG,查了半天
才想到是 LINQ 延遲查詢 造成的
LINQ 查詢結果 搭配 foreach 作資料操作上的小問題
LINQ 延遲查詢,有很多前輩都有寫文章作說明講解
雖然有些細節我還無法吸收
小朱® 的技術隨手寫-前輩的 [.NET] LINQ 的延遲執行 (Deferred Execution)
In 91-前輩的 [.NET]快快樂樂學LINQ系列前哨戰-延遲執行 (Deferred Execution)
兩位前輩講解比較深入
而實際應用上會遇到的問題,可以參考
Huan-Lin 學習筆記-前輩的 《C# 本事》摘錄:LINQ (2)
參考資料網路上還有許多,我就不一一列表
以下是我發生的情況
簡化模擬出我工作的情況,當然條件沒這麼簡單,因為物件的參照,以及Where條件的複雜
這BUG我查了許久,才想到是延遲查詢造成的
List<int> testDatas = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int condition = 1; var selectDatas = testDatas.Where(i => i > condition); int times = 0; foreach (var oneData in selectDatas) { ++times; Console.WriteLine($"第{times}次 查詢結果數量:{selectDatas.Count()}"); condition += 2; Console.WriteLine($"變更條件後 查詢結果數量:{selectDatas.Count()}"); Console.WriteLine("======="); }
正常來想,已查詢後的結果,就算去動到查詢條件
應該也不會變更到查詢結果
會認為結果如下圖.1
但是因為延遲查詢的特性
上面的程式碼執行結果會如下圖.2
並不會如預想的查詢結果不變
解決方是很簡單,就是把查詢結果ToList就可以避免變化
var selectDatas = testDatas.Where(i => i > condition).ToList();
執行結果就會如圖.1
為了以後不要再發生同樣問題
只要記的把使用LINQ後的結果
如果要使用foreach 都轉換成List即可
但這時就想到 基本上LINQ延遲查詢特性
它會再 foreach 每次都查詢一次
所以效能到底是如何呢
我簡單的測試一下,測試如下
List<int> testDatas = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; Stopwatch sw = new Stopwatch(); for (int j = 0; j < 10; ++j) { // 第一種測試 sw.Reset(); sw.Start(); int condition = 1; var selectDatas = testDatas.Where(i => i > condition); for (int i = 0; i < 1000000; ++i) { foreach (var t in selectDatas) { int temp = t; ++temp; } } sw.Stop(); Console.WriteLine($"第{j}次 直接查詢後foreach {sw.ElapsedMilliseconds}"); // 第二種測試 sw.Reset(); sw.Start(); selectDatas = testDatas.Where(i => i > condition).ToList(); for (int i = 0; i < 1000000; ++i) { foreach (var t in selectDatas) { int temp = t; ++temp; } } sw.Stop(); Console.WriteLine($"第{j}次 查詢後ToList {sw.ElapsedMilliseconds}"); // 第三種測試 sw.Reset(); sw.Start(); selectDatas = testDatas.Where(i => i > condition); for (int i = 0; i < 1000000; ++i) { var tempList = selectDatas.ToList(); foreach (var t in tempList) { int temp = t; ++temp; } } sw.Stop(); Console.WriteLine($"第{j}次 查詢後在迴圈裡ToList {sw.ElapsedMilliseconds}"); Console.WriteLine("====="); }
結果如下圖
結論
ToList後再去 foreach效能比較好,理由也很簡單
因為並不會每次在去重新查詢。
所以建議查詢結果需要去 foreach作資料操作 都先轉換成List
當然ToList轉換一定也有他的效能消耗。所以要判斷在哪邊使用
像我第三種測試 故意在迴圈裡面 ToList 速度明顯變慢很多