2017-04-21 79 views
-1

我需要知道哪種方法更快。在C#中迭代IQueryable時的性能

// Approach1 
var filteredList = MyList.Where(x => x.IsNull); 
foreach (var item in filteredList) 
{ 
    // Do someghing... 
} 

// Approach2 
foreach (var item in MyList.Where(x => x.IsNull)) 
{ 
    // Do someghing... 
} 

是否是最後一個執行Where每次迭代foreach

+5

不,這將是荒謬的。在每次迭代中獲得新迭代器有什麼意義? –

+0

通過添加一個你正在得到不同執行的地方,因爲你現在正在IEnumerable上進行操作,不管怎樣,在每次迭代中檢查條件,但是如果你需要多次使用該條件可枚舉, IEnumerable回到列表 –

+0

他們完全一樣 –

回答

6

這兩者幾乎沒有區別。

在這個版本中

// Approach1 
var filteredList = MyList.Where(x => x.IsNull); 
foreach (var item in filteredList) 
{ 
    // Do someghing... 
} 

你基本上是說

  1. 從某處
  2. 獲取一個迭代器(IEnumerable<T>)分配一個參考該迭代變量。
  3. 遍歷迭代器

在這個版本中

// Approach2 
foreach (var item in MyList.Where(x => x.IsNull)) 
{ 
    // Do someghing... 
} 

你基本上是說

  1. 獲取從某處
  2. 迭代的迭代器(IEnumerable<T>)在迭代

所以差異非常小 - 方法1中唯一附加的是將引用變量賦值給另一個引用變量,這是一個在內存中移動幾個字節的問題。

更重要的是,編譯器可能只是優化變量filteredList,在這種情況下,它將爲這兩種方法發出完全相同的IL。

+5

...這可能會被編譯器優化掉。 – GalacticCowboy

+0

*這是一個在內存中移動四個字節的問題... *考慮32位機器(OS) – Rahul

+0

這正是我的答案顯示在下面@john將 – loneshark99

0

兩個循環都是相同的。

Where()的代碼執行對MyList上的每個項目執行 - 您已將其指定爲判斷列表中的元素是否應包含在結果中的條件。

所以,

x => x.IsNull 

執行用於MYLIST每一個元素,但你的foreach循環體:

// Do something 

將只執行對那些通過標準的元素,這將是分配給項目。也就是說,每次執行foreach循環時,它「看到」的項目都會有item.IsNull爲true。

2

檢查上Enumerable.Where()的文檔,因爲它引用:

通過該方法表示的查詢直到對象 枚舉通過直接或 調用其GetEnumerator方法通過在Visual C#

使用的foreach執行

所以基本上,有您已發佈這兩個方法之間沒有區別

3

值得再ading了on how foreach works(不只是這個問題,但普遍關心的問題):它從該表達式的結果一個枚舉(令人驚訝的廣義定義)的in權,然後對調查員要求bool MoveNext()直到MoveNext()回報假。

這也就沒有什麼意義foreach反覆評估它從枚舉的表達。事實上,.NET會竭盡全力阻止您在枚舉過程中更改序列 - 換句話說,要確保它正在枚舉與它開始的完全相同的事情。嘗試將它添加項目到List<T>,而你在foreach:

var list = Enumerable.Range(0, 10).ToList(); 

foreach (var x in list) 
{ 
    list.Add(x); 
} 

「集合被修改;枚舉操作可能不會執行。」

沒有銷售。

而且如果它獲得新的統計員,那就要跳過他們的項目。第一次迭代,抓住第一個項目。第二:抓住一個新的統計員,跳過一個,接下來。第三:抓住一個新的統計員,跳過兩個,跳下一座橋。

這是瘋了。專業人士無法編寫代碼,甚至無法清醒的業餘愛好者。

所以,它只是使用表達式的結果。無論表達式是否在parens之間,或者是否將其結果分配給局部變量都無關緊要。

兩個版本是相同的。

還有可能存在開放式枚舉的可能性 - 在那裏可能會有一個並行線程,排隊從設備驅動程序或其他東西進來的東西,枚舉可能不一定會結束。但是你仍然可以從任一版本的代碼中獲得相同的枚舉器。

+1

喜歡*集合修改的例子... * – Rahul

0

它們都是相同的,這就是迭代器的美妙之處。你也可以檢查它被編譯的IL,這在LINQPAD中是一樣的。

IL_0033: ldloc.0  // list 
IL_0034: ldsfld  UserQuery+<>c.<>9__0_0 
IL_0039: dup   
IL_003A: brtrue.s IL_0053 
IL_003C: pop   
IL_003D: ldsfld  UserQuery+<>c.<>9 
IL_0042: ldftn  UserQuery+<>c.<Main>b__0_0 
IL_0048: newobj  System.Func<UserQuery+Test,System.Boolean>..ctor 
IL_004D: dup   
IL_004E: stsfld  UserQuery+<>c.<>9__0_0 
IL_0053: call  System.Linq.Enumerable.Where<Test> 
IL_0058: stloc.3  // filteredList 
IL_0059: nop   
IL_005A: ldloc.3  // filteredList 
IL_005B: callvirt System.Collections.Generic.IEnumerable<UserQuery+Test>.GetEnumerator 
IL_0060: stloc.s  04 
IL_0062: br.s  IL_0077 
IL_0064: ldloc.s  04 
IL_0066: callvirt System.Collections.Generic.IEnumerator<UserQuery+Test>.get_Current 
IL_006B: stloc.s  05 // item 
IL_006D: nop   
IL_006E: ldloc.s  05 // item 
IL_0070: call  LINQPad.Extensions.Dump<Test> 
IL_0075: pop   
IL_0076: nop   
IL_0077: ldloc.s  04 
IL_0079: callvirt System.Collections.IEnumerator.MoveNext 
IL_007E: brtrue.s IL_0064 
IL_0080: leave.s  IL_008F 
IL_0082: ldloc.s  04 
IL_0084: brfalse.s IL_008E 
IL_0086: ldloc.s  04 
IL_0088: callvirt System.IDisposable.Dispose 
IL_008D: nop   
IL_008E: endfinally 
IL_008F: nop   


IL_0090: ldloc.0  // list 
IL_0091: ldsfld  UserQuery+<>c.<>9__0_1 
IL_0096: dup   
IL_0097: brtrue.s IL_00B0 
IL_0099: pop   
IL_009A: ldsfld  UserQuery+<>c.<>9 
IL_009F: ldftn  UserQuery+<>c.<Main>b__0_1 
IL_00A5: newobj  System.Func<UserQuery+Test,System.Boolean>..ctor 
IL_00AA: dup   
IL_00AB: stsfld  UserQuery+<>c.<>9__0_1 
IL_00B0: call  System.Linq.Enumerable.Where<Test> 
IL_00B5: callvirt System.Collections.Generic.IEnumerable<UserQuery+Test>.GetEnumerator 
IL_00BA: stloc.s  06 
IL_00BC: br.s  IL_00D1 
IL_00BE: ldloc.s  06 
IL_00C0: callvirt System.Collections.Generic.IEnumerator<UserQuery+Test>.get_Current 
IL_00C5: stloc.s  07 // item 
IL_00C7: nop   
IL_00C8: ldloc.s  07 // item 
IL_00CA: call  LINQPad.Extensions.Dump<Test> 


void Main() 
{ 
    List<Test> list = new List<Test>(); 
    var x1 = new Test() { IsNull = false }; 
    var x2 = new Test() { IsNull = true }; 
    list.Add(x1); 
    list.Add(x2); 
    // Approach1 
    var filteredList = list.Where(x => x.IsNull == true); 
    foreach (var item in filteredList) 
    { 
     item.Dump(); 
    } 
    // Approach2 
    foreach (var item in list.Where(x => x.IsNull == true)) 
    { 
     item.Dump(); 
    } 
}