gpt4 book ai didi

c# - foreach 在 string[] 与 List 的幕后

转载 作者:行者123 更新时间:2023-11-30 16:37:30 25 4
gpt4 key购买 nike

为什么在 CIL 中,编译器将 foreach循环到 for使用数组时循环,但在 List<T> 时使用迭代器模式是用的吗?

如果两者都是System.ArraySystem.Collections.Generic.List<T>实现 IEnumerable ,他们不应该都在幕后使用迭代器模式吗?

这是一个例子:

控制台 App1:

C#:

class Program
{
static void Main(string[] args)
{
var enumerable = new List<string> { "a", "b" };

foreach (string item in enumerable)
{
string x = item;
}
}
}

CIL:

.method private hidebysig static 
void Main (
string[] args
) cil managed
{
// Method begins at RVA 0x2050
// Code size 80 (0x50)
.maxstack 3
.entrypoint
.locals init (
[0] class [mscorlib]System.Collections.Generic.List`1<string> enumerable,
[1] valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>,
[2] string item,
[3] string x
)

IL_0000: nop
IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1<string>::.ctor()
IL_0006: dup
IL_0007: ldstr "a"
IL_000c: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<string>::Add(!0)
IL_0011: nop
IL_0012: dup
IL_0013: ldstr "b"
IL_0018: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<string>::Add(!0)
IL_001d: nop
IL_001e: stloc.0
IL_001f: nop
IL_0020: ldloc.0
IL_0021: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<string>::GetEnumerator()
IL_0026: stloc.1
.try
{
IL_0027: br.s IL_0035
// loop start (head: IL_0035)
IL_0029: ldloca.s 1
IL_002b: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>::get_Current()
IL_0030: stloc.2
IL_0031: nop
IL_0032: ldloc.2
IL_0033: stloc.3
IL_0034: nop

IL_0035: ldloca.s 1
IL_0037: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>::MoveNext()
IL_003c: brtrue.s IL_0029
// end loop

IL_003e: leave.s IL_004f
} // end .try
finally
{
IL_0040: ldloca.s 1
IL_0042: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>
IL_0048: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_004d: nop
IL_004e: endfinally
} // end handler

IL_004f: ret
} // end of method Program::Main

控制台应用 2:

C#:

class Program
{
static void Main(string[] args)
{
var enumerable = new string[] { "a", "b" };

foreach (string item in enumerable)
{
string x = item;
}
}
}

CIL:

.method private hidebysig static 
void Main (
string[] args
) cil managed
{
// Method begins at RVA 0x2050
// Code size 51 (0x33)
.maxstack 4
.entrypoint
.locals init (
[0] string[] enumerable,
[1] string[],
[2] int32,
[3] string item,
[4] string x
)

IL_0000: nop
IL_0001: ldc.i4.2
IL_0002: newarr [mscorlib]System.String
IL_0007: dup
IL_0008: ldc.i4.0
IL_0009: ldstr "a"
IL_000e: stelem.ref
IL_000f: dup
IL_0010: ldc.i4.1
IL_0011: ldstr "b"
IL_0016: stelem.ref
IL_0017: stloc.0
IL_0018: nop
IL_0019: ldloc.0
IL_001a: stloc.1
IL_001b: ldc.i4.0
IL_001c: stloc.2
IL_001d: br.s IL_002c
// loop start (head: IL_002c)
IL_001f: ldloc.1
IL_0020: ldloc.2
IL_0021: ldelem.ref
IL_0022: stloc.3
IL_0023: nop
IL_0024: ldloc.3
IL_0025: stloc.s x
IL_0027: nop
IL_0028: ldloc.2
IL_0029: ldc.i4.1
IL_002a: add
IL_002b: stloc.2

IL_002c: ldloc.2
IL_002d: ldloc.1
IL_002e: ldlen
IL_002f: conv.i4
IL_0030: blt.s IL_001f
// end loop

IL_0032: ret
} // end of method Program::Main

最佳答案

不同之处在于,对于数组,没有分配对象来管理迭代,并且删除了边界检查。对于列表,迭代管理变量是堆栈分配的,并且执行边界检查。因此,很明显为什么语言设计者在迭代时对数组使用 For 循环(更改 IL 输出中的 ForEach)。

由于数组不支持添加/删除项,因此存在隐含的固定长度。因此,在没有边界检查的情况下,这是一种通过索引而不是迭代器(IEnumerable 实现)访问数组项的优化。

关于c# - foreach 在 string[] 与 List<string> 的幕后,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57940788/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com