在c#中迭代IQueryable时的性能

时间:2017-04-21 19:31:11

标签: c#

我需要知道哪种方法更快。

// 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 :(得分:6)

这两者之间几乎没有区别。

在此版本中

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

你基本上是在说

  1. 从某处获取迭代器(IEnumerable<T>
  2. 将对该迭代器的引用分配给变量。
  3. 迭代迭代器
  4. 在此版本中

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

    你基本上是在说

    1. 从某处获取迭代器(IEnumerable<T>
    2. 迭代迭代器
    3. 所以区别非常小 - 在approach1中唯一的附加是将引用变量赋值给另一个引用变量,这是在内存中移动几个字节的问题。

      此外,编译器可能只是优化变量filteredList,在这种情况下,它将为两种方法发出完全相同的IL。

答案 1 :(得分:3)

值得阅读on how foreach works(不仅仅是针对这个问题,而是作为一个普遍关注的问题):它从表达式的结果到右边得到一个枚举器(令人惊讶的广泛定义) in的{​​{1}},然后在该枚举器上调用bool MoveNext(),直到MoveNext()返回false。

foreach重复评估它从枚举器获取的表达式是没有意义的。实际上,.NET会不遗余力地阻止您在枚举时更改序列 - 换句话说,确保它在枚举时完全相同。尝试将项目添加到List<T>,同时您还可以使用该项目:

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

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

&#34;收藏被修改;枚举操作可能无法执行。&#34;

没有销售。

如果它获得新的枚举器,则必须跳过其中的项目。第一次迭代,抓住第一项。第二:抓住一个新的普查员,跳过一个,接下来。第三:抓住一个新的普查员,跳过两个,跳下一座桥。

那是疯狂的。专业人士无法编写代码,甚至是半途而废的业余爱好者。

所以,它只是使用你表达的结果。表达式是否恰好位于parens之间,或者是否将其结果分配给局部变量并不重要。

两个版本都是一样的。

还有可能开放式枚举 - 可能存在于并行线程中的某些东西,排队来自设备驱动程序或其他东西的东西,并且枚举可能不一定结束。但您仍然可以从任一版本的代码获得相同的枚举器。

答案 2 :(得分:2)

检查Enumerable.Where()上的文档,因为它引用了:

  

此方法表示的查询直到该对象才会执行   通过直接调用其GetEnumerator方法或枚举来枚举   在Visual C#中使用foreach#

基本上,你发布的两种方法没有区别

答案 3 :(得分:0)

两个循环都是相同的。

Where()中的代码对MyList上的每一项都执行 - 您已将其指定为判断列表中的元素是否应包含在结果

所以,

x => x.IsNull

对MyList中的每个元素执行,但是对foreach循环的主体执行:

// Do something

仅对那些通过条件的元素执行,并将分配给item。也就是说,每次你的foreach循环执行时,它&#34;看到&#34;将有item.IsNull为真。

答案 4 :(得分: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();
    }
}