为什么C#编译器用foreach语句单独处理字符串类

本文关键字:单独 处理 字符串 语句 foreach 编译器 为什么 | 更新日期: 2023-09-27 18:01:11

我清楚地理解在处理foreach语句时使用C#编译器的"基于模式"方法。

从C#语言规范(第8.8.4节(可以清楚地看出,首先C#编译器试图找到GetEnumerator方法,然后才试图找到IEnumerable<T>IEnumerable接口。

但我不清楚为什么C#编译器单独处理string(因为String类包含一个返回CharEnumerator的方法GetEnumerator,它还实现了IEnumerable<char>IEnumerable接口(:

string s = "1234";
foreach(char c in s)
  Console.WriteLine(c);

转换为

string s = "1234";
for(int i = 0; i < s.Length; i++)
  Console.WriteLine(s[i]);

但是我在语言规范中找不到任何关于String类的异常。有人能对这个解决方案提供一些见解吗?

我试过使用C#4编译器。以下是上一个代码片段的IL代码:

IL_0000:  ldstr       "1234"
IL_0005:  stloc.0     
IL_0006:  ldloc.0     
IL_0007:  stloc.2     
IL_0008:  ldc.i4.0    
IL_0009:  stloc.3     
IL_000A:  br.s        IL_001E
IL_000C:  ldloc.2     
IL_000D:  ldloc.3     
IL_000E:  callvirt    System.String.get_Chars
IL_0013:  stloc.1     
IL_0014:  ldloc.1     
IL_0015:  call        System.Console.WriteLine
IL_001A:  ldloc.3     
IL_001B:  ldc.i4.1    
IL_001C:  add         
IL_001D:  stloc.3     
IL_001E:  ldloc.3     
IL_001F:  ldloc.2     
IL_0020:  callvirt    System.String.get_Length
IL_0025:  blt.s       IL_000C

为什么C#编译器用foreach语句单独处理字符串类

好的捕获。我知道编译器对数组进行了类似的优化,但我不知道它对字符串也这样做了。

我能给你的最好的结果是从语言规范中调用,只要编译器产生等效的行为,它就有权偏离"正典":

8.8.4 foreach语句

[…]形式的foreach语句foreach (V v in x)嵌入语句然后扩展为:

{
    E e = ((C)(x)).GetEnumerator();
    try {
        V v;
        while (e.MoveNext()) {
            v = (V)(T)e.Current;
            embedded-statement
        }
    }
    finally {
        … // Dispose e
    }
}

[…]允许执行实现给定的foreach语句不同,例如性能原因,只要行为与上述扩展一致