Почему компилятор С# обрабатывал класс строк отдельно с помощью инструкции foreach
Я четко понимаю подход, основанный на шаблонах, который использует компилятор С#, когда он имеет дело с оператором foreach
.
И из спецификации языка С# (раздел 8.8.4) ясно, что в первую очередь компилятор С# пытается найти метод GetEnumerator
и только затем пытается найти интерфейсы IEnumerable<T>
и IEnumerable
.
Но его непонятно для меня, почему компилятор С# рассматривает 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
. Может ли кто-нибудь дать некоторое представление об этом решении?
Я попытался с компилятором С# 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
Хорошая добыча. Я знал, что компилятор выполнил аналогичную оптимизацию для массивов, но я не знал, что это тоже для строк.
Лучшее, что я могу вам получить, это вызов из спецификации языка, который дает компилятору право отклониться от "canon", если он производит эквивалентное поведение:
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 по-разному, например. для выполнения причинам, пока поведение в соответствии с вышеупомянутым расширением.