Почему компилятор C # обрабатывает строковый класс отдельно с помощью оператора foreach - PullRequest
21 голосов
/ 04 июля 2011

Я четко понимаю "основанный на шаблонах" подход , который использует компилятор C # при работе с оператором foreach.

И из спецификации языка C # (раздел 8.8.4)Понятно, что в первую очередь компилятор C # пытается найти метод GetEnumerator и только потом пытается найти интерфейсы IEnumerable<T> и IEnumerable.

Но мне неясно, почему компилятор C # обрабатывает string отдельно(поскольку класс String содержит метод GetEnumerator, который возвращает CharEnumerator, а также реализует IEnumerable<char> и IEnumerable interfces):

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

1 Ответ

21 голосов
/ 04 июля 2011

Хороший улов.Я знал, что компилятор выполнил аналогичную оптимизацию для массивов, но я не знал, что он делал это и для строк.

Лучшее, что я могу вам дать, - это вызов из спецификации языка , который дает компилятору право отклоняться от 'канона', пока он производит эквивалентное поведение:

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 по-разному, например, из соображений производительности, если поведениев соответствии с приведенным выше расширением.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...