2015-10-08 34 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指令不执行空校验,这就是为什么它是慢。