我需要知道哪种方法更快。
// 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
?
答案 0 :(得分:6)
这两者之间几乎没有区别。
在此版本中
// Approach1
var filteredList = MyList.Where(x => x.IsNull);
foreach (var item in filteredList)
{
// Do someghing...
}
你基本上是在说
IEnumerable<T>
)在此版本中
// Approach2
foreach (var item in MyList.Where(x => x.IsNull))
{
// Do someghing...
}
你基本上是在说
IEnumerable<T>
)所以区别非常小 - 在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();
}
}