2015-10-08 37 views
7

今天我發現了一些我不太明白的東西。我在LinqPad的following code(5版):Foreach集合投向IEnumerable的工作比沒有投射慢?

void Main() 
{ 
    const int size = 5000000; 
    List<Thing> things = Enumerable.Range(1, 5000000).Select(x => new Thing {Id = x}).ToList(); 

    var sw1 = Stopwatch.StartNew(); 
    foreach (var t in things) 
     if(t.Id == size) break; 
    sw1.ElapsedMilliseconds.Dump(); 

    var sw2 = Stopwatch.StartNew(); 
    IEnumerable<Thing> ienThings = things; 
    foreach (var t in ienThings) 
     if (t.Id == size) break; 
    sw2.ElapsedMilliseconds.Dump(); 

} 

class Thing 
{ 
    public long Id { get; set; } 
} 

看來,第二個循環需要兩次只要第一個。爲什麼這個簡單的演員會產生這樣的效果?我敢肯定,有一些簡單的事情發生在我不知所措的情況下。

+0

如果你真的想要比較2個循環 - 至少移動var sw2 = Stopwatch.StartNew();下面一行來省略你的新集合所需的時間計算 –

+0

@SerhiyChupryk這不是一個新的集合,它只是一個對同一個集合的不同類型的引用。運行該線的時間應該是無關緊要的。 – juharr

+1

你應該檢查這個編譯的IL。我在類似的時間獲得了500,000,50,000,000和500,000,000個項目,這強烈表明這個循環正在被優化。 –

回答

6

這是由於使用的指令callcallvirt之間的差異。

call  System.Collections.Generic.List<UserQuery+Thing>+Enumerator.get_Current 
call  System.Collections.Generic.List<UserQuery+Thing>+Enumerator.MoveNext 

VS

callvirt System.Collections.Generic.IEnumerator<UserQuery+Thing>.get_Current 
callvirt System.Collections.IEnumerator.MoveNext 

callvirt指令不執行空校驗,這就是爲什麼它是慢。