C#中“ for”和“ foreach”控制结构的性能差异

哪个代码段可以提供更好的性能?以下代码段是用C#编写的。


1。


for(int counter=0; counter<list.Count; counter++)

{

    list[counter].DoSomething();

}

2。


foreach(MyType current in list)

{

    current.DoSomething();

}


jeck猫
浏览 260回答 3
3回答

慕桂英3389331

好吧,部分取决于的确切类型list。它还取决于您使用的确切CLR。它是否有意义,将取决于您是否在循环中进行任何实际工作。在几乎所有情况下,对性能的影响都不是很大,但对可读性的影响则有利于foreach循环。我个人也会使用LINQ来避免出现“ if”:foreach (var item in list.Where(condition)){}编辑:对于那些声称对List<T>with进行迭代foreach产生与for循环相同代码的人来说,以下证据表明它不会:static void IterateOverList(List<object> list){&nbsp; &nbsp; foreach (object o in list)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; Console.WriteLine(o);&nbsp; &nbsp; }}产生IL:.method private hidebysig static void&nbsp; IterateOverList(class [mscorlib]System.Collections.Generic.List`1<object> list) cil managed{&nbsp; // Code size&nbsp; &nbsp; &nbsp; &nbsp;49 (0x31)&nbsp; .maxstack&nbsp; 1&nbsp; .locals init (object V_0,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<object> V_1)&nbsp; IL_0000:&nbsp; ldarg.0&nbsp; IL_0001:&nbsp; callvirt&nbsp; &nbsp;instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<object>::GetEnumerator()&nbsp; IL_0006:&nbsp; stloc.1&nbsp; .try&nbsp; {&nbsp; &nbsp; IL_0007:&nbsp; br.s&nbsp; &nbsp; &nbsp; &nbsp;IL_0017&nbsp; &nbsp; IL_0009:&nbsp; ldloca.s&nbsp; &nbsp;V_1&nbsp; &nbsp; IL_000b:&nbsp; call&nbsp; &nbsp; &nbsp; &nbsp;instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<object>::get_Current()&nbsp; &nbsp; IL_0010:&nbsp; stloc.0&nbsp; &nbsp; IL_0011:&nbsp; ldloc.0&nbsp; &nbsp; IL_0012:&nbsp; call&nbsp; &nbsp; &nbsp; &nbsp;void [mscorlib]System.Console::WriteLine(object)&nbsp; &nbsp; IL_0017:&nbsp; ldloca.s&nbsp; &nbsp;V_1&nbsp; &nbsp; IL_0019:&nbsp; call&nbsp; &nbsp; &nbsp; &nbsp;instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<object>::MoveNext()&nbsp; &nbsp; IL_001e:&nbsp; brtrue.s&nbsp; &nbsp;IL_0009&nbsp; &nbsp; IL_0020:&nbsp; leave.s&nbsp; &nbsp; IL_0030&nbsp; }&nbsp; // end .try&nbsp; finally&nbsp; {&nbsp; &nbsp; IL_0022:&nbsp; ldloca.s&nbsp; &nbsp;V_1&nbsp; &nbsp; IL_0024:&nbsp; constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<object>&nbsp; &nbsp; IL_002a:&nbsp; callvirt&nbsp; &nbsp;instance void [mscorlib]System.IDisposable::Dispose()&nbsp; &nbsp; IL_002f:&nbsp; endfinally&nbsp; }&nbsp; // end handler&nbsp; IL_0030:&nbsp; ret} // end of method Test::IterateOverList编译器对数组的处理方式不同,foreach基本上将for循环转换为循环,但不是List<T>。这是数组的等效代码:static void IterateOverArray(object[] array){&nbsp; &nbsp; foreach (object o in array)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; Console.WriteLine(o);&nbsp; &nbsp; }}// Compiles into....method private hidebysig static void&nbsp; IterateOverArray(object[] 'array') cil managed{&nbsp; // Code size&nbsp; &nbsp; &nbsp; &nbsp;27 (0x1b)&nbsp; .maxstack&nbsp; 2&nbsp; .locals init (object V_0,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;object[] V_1,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;int32 V_2)&nbsp; IL_0000:&nbsp; ldarg.0&nbsp; IL_0001:&nbsp; stloc.1&nbsp; IL_0002:&nbsp; ldc.i4.0&nbsp; IL_0003:&nbsp; stloc.2&nbsp; IL_0004:&nbsp; br.s&nbsp; &nbsp; &nbsp; &nbsp;IL_0014&nbsp; IL_0006:&nbsp; ldloc.1&nbsp; IL_0007:&nbsp; ldloc.2&nbsp; IL_0008:&nbsp; ldelem.ref&nbsp; IL_0009:&nbsp; stloc.0&nbsp; IL_000a:&nbsp; ldloc.0&nbsp; IL_000b:&nbsp; call&nbsp; &nbsp; &nbsp; &nbsp;void [mscorlib]System.Console::WriteLine(object)&nbsp; IL_0010:&nbsp; ldloc.2&nbsp; IL_0011:&nbsp; ldc.i4.1&nbsp; IL_0012:&nbsp; add&nbsp; IL_0013:&nbsp; stloc.2&nbsp; IL_0014:&nbsp; ldloc.2&nbsp; IL_0015:&nbsp; ldloc.1&nbsp; IL_0016:&nbsp; ldlen&nbsp; IL_0017:&nbsp; conv.i4&nbsp; IL_0018:&nbsp; blt.s&nbsp; &nbsp; &nbsp; IL_0006&nbsp; IL_001a:&nbsp; ret} // end of method Test::IterateOverArray有趣的是,我在任何地方都找不到C#3规范中记录的内容...

烙印99

一个for循环被编译的代码大约相当于这个:int tempCount = 0;while (tempCount < list.Count){&nbsp; &nbsp; if (list[tempCount].value == value)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; // Do something&nbsp; &nbsp; }&nbsp; &nbsp; tempCount++;}其中,将foreach循环编译为大致等效于此的代码:using (IEnumerator<T> e = list.GetEnumerator()){&nbsp; &nbsp; while (e.MoveNext())&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; T o = (MyClass)e.Current;&nbsp; &nbsp; &nbsp; &nbsp; if (row.value == value)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Do something&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}正如您所看到的,这都取决于枚举器的实现方式以及列表索引器的实现方式。事实证明,基于数组的类型的枚举数通常是这样写的:private static IEnumerable<T> MyEnum(List<T> list){&nbsp; &nbsp; for (int i = 0; i < list.Count; i++)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; yield return list[i];&nbsp; &nbsp; }}正如您所看到的,在这种情况下,它并没有太大的区别,但是链表的枚举数可能看起来像这样:private static IEnumerable<T> MyEnum(LinkedList<T> list){&nbsp; &nbsp; LinkedListNode<T> current = list.First;&nbsp; &nbsp; do&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; yield return current.Value;&nbsp; &nbsp; &nbsp; &nbsp; current = current.Next;&nbsp; &nbsp; }&nbsp; &nbsp; while (current != null);}在.NET中,您会发现LinkedList <T>类甚至没有索引器,因此您将无法在链表上进行for循环;但是如果可以的话,索引器的编写必须像这样:public T this[int index]{&nbsp; &nbsp; &nbsp; &nbsp;LinkedListNode<T> current = this.First;&nbsp; &nbsp; &nbsp; &nbsp;for (int i = 1; i <= index; i++)&nbsp; &nbsp; &nbsp; &nbsp;{&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; current = current.Next;&nbsp; &nbsp; &nbsp; &nbsp;}&nbsp; &nbsp; &nbsp; &nbsp;return current.value;}如您所见,在循环中多次调用此方法要比使用可以记住它在列表中位置的枚举器慢得多。
打开App,查看更多内容
随时随地看视频慕课网APP