Это тип, сгенерированный компилятором. Компилятор генерирует реализацию IEnumerator<string>
, которая возвращает три значения «a», и класс скелета IEnumerable<string>
, который предоставляет одно из них в своем методе GetEnumerator
.
Сгенерированный код выглядит что-то вот так *:
// No idea what the naming convention for the generated class is --
// this is really just a shot in the dark.
class GetIEnumerable_Enumerator : IEnumerator<string>
{
int _state;
string _current;
public bool MoveNext()
{
switch (_state++)
{
case 0:
_current = "a";
break;
case 1:
_current = "a";
break;
case 2:
_current = "a";
break;
default:
return false;
}
return true;
}
public string Current
{
get { return _current; }
}
object IEnumerator.Current
{
get { return Current; }
}
void IEnumerator.Reset()
{
// not sure about this one -- never really tried it...
// I'll just guess
_state = 0;
_current = null;
}
}
class GetIEnumerable_Enumerable : IEnumerable<string>
{
public IEnumerator<string> GetEnumerator()
{
return new GetIEnumerable_Enumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Или, может быть, как SLaks говорит в своем ответе, две реализации заканчиваются в одном классе. Я написал это на основе изменчивой памяти сгенерированного кода, на которую смотрел раньше; на самом деле, одного класса будет достаточно, поскольку нет причин, по которым вышеуказанная функциональность требует двух.
На самом деле, если задуматься, две реализации на самом деле должны находиться в одном классе, так как я только что вспомнил, что функции, использующие операторы yield
, должны иметь тип возврата или IEnumerable<T>
или IEnumerator<T>
.
В любом случае, я позволю вам внести исправления кода в то, что я написал мысленно.
* Это исключительно для иллюстрации; Я не претендую на его реальную точность. Это всего лишь общая демонстрация того, как компилятор делает то, что он делает, основываясь на доказательствах, которые я видел в своих собственных исследованиях.