一尘不染

foreach是否在每次迭代时都评估数组?

c#

我想创建一个foreach跳过第一项的。我在其他地方看到过,最简单的方法是使用myCollection.Skip(1),但是我有一个问题:

MSDN文档上.Skip()描述它“在序列中绕过指定数量的元素,然后返回其余元素”。这是否意味着

foreach(object i in myCollection.Skip(1))
{ ... }

程序.Skip(1)每次foreach迭代都必须执行吗?还是foreach(有点像switch)不需要对数组进行多次求值?

创建一个虚拟对象var _dummy = myCollection.Skip(1)并对其进行迭代会更有效吗?


阅读 219

收藏
2020-05-19

共1个答案

一尘不染

我只是以此来嘲笑你的代码

foreach(var v in Enumerable.Range(1,10).Skip(1))
    v.Dump();

这是生成的IL。

IL_0001:  nop         
IL_0002:  ldc.i4.1    
IL_0003:  ldc.i4.s    0A 
IL_0005:  call        System.Linq.Enumerable.Range
IL_000A:  ldc.i4.1    
IL_000B:  call        System.Linq.Enumerable.Skip//Call to Skip
IL_0010:  callvirt    System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator
IL_0015:  stloc.1     // CS$5$0000
IL_0016:  br.s        IL_0026
IL_0018:  ldloc.1     // CS$5$0000
IL_0019:  callvirt    System.Collections.Generic.IEnumerator<System.Int32>.get_Current
IL_001E:  stloc.0     // v
IL_001F:  ldloc.0     // v
IL_0020:  call        LINQPad.Extensions.Dump
IL_0025:  pop         
IL_0026:  ldloc.1     // CS$5$0000
IL_0027:  callvirt    System.Collections.IEnumerator.MoveNext
IL_002C:  stloc.2     // CS$4$0001
IL_002D:  ldloc.2     // CS$4$0001
IL_002E:  brtrue.s    IL_0018
IL_0030:  leave.s     IL_0042
IL_0032:  ldloc.1     // CS$5$0000
IL_0033:  ldnull      
IL_0034:  ceq         
IL_0036:  stloc.2     // CS$4$0001
IL_0037:  ldloc.2     // CS$4$0001
IL_0038:  brtrue.s    IL_0041
IL_003A:  ldloc.1     // CS$5$0000
IL_003B:  callvirt    System.IDisposable.Dispose
IL_0040:  nop         
IL_0041:  endfinally

如您所见Skip,仅被调用一次。

等效的C#代码看起来像这样

IEnumerator<int> e = ((IEnumerable<int>)values).GetEnumerator();//Get the enumerator
try
{
  int m;//This variable is here prior to c#5.0
  while(e.MoveNext())
  {//int m; is declared here starting from c#5.0
    m = (int)(int)e.Current;
    //Your code here
  }
}
finally
{
  if (e != null) ((IDisposable)e).Dispose();
}

考虑下面的代码,如果foreach
VeryLongRunningMethodThatReturnsEnumerable在每次迭代中调用,那将是一场噩梦。语言设计中的巨大缺陷。幸运的是它没有那样做。

foreach(var obj in VeryLongRunningMethodThatReturnsEnumerable())
{
   //Do something with that obj
}
2020-05-19