Приведение из IEnumerable в IEnumerator
Я играю с IEnumerable/<T>
а также IEnumerable/<T>
, В одном из моих испытаний я попытался присвоить возвращаемое значение типа IEnumerable<T>
до значения IEnumerator<T>
с помощью приведения, а затем пытается выполнить MoveNext()
а также Current
, Хотя кастинг не выдал ошибок, я не получил вывод:
class Animal
{
public string AnimalType { get; set; }
public Animal(string animal_type)
{
AnimalType = animal_type;
}
}
class FarmCollection
{
readonly Animal[] _farm =
{ new Animal("duck"), new Animal("cow"), new Animal("sheep") };
public IEnumerable<Animal> GetEnumerable()
{
foreach (Animal a in _farm)
yield return a;
}
}
class Test
{
public static void Main()
{
FarmCollection farm = new FarCollection();
IEnumerator<Animal> rator = (IEnumerator<Animal>)farm.GetEnumerable();
while (rator.MoveNext())
{
Animal a = (Animal)rator.Current;
Console.WriteLine(a.AnimalType);
}
}
}
Первый вопрос: почему я не получил вывод, а Main просто возвращается?
Второй вопрос: почему кастинг от IEnumerable<Animal>
в IEnumerator<Animal>
не выдает ошибку компиляции?
2 ответа
Вот как твоя FarmCollection.GetEnumerable
Метод выглядит так, как будто он декомпилирован:
public IEnumerable<Animal> GetEnumerable()
{
FarmCollection.<GetEnumerable>d__0 <GetEnumerable>d__ =
new FarmCollection.<GetEnumerable>d__0(-2);
<GetEnumerable>d__.<>4__this = this;
return <GetEnumerable>d__;
}
Тип FarmCollection.<GetEnumerable>d__0
также генерируется компилятором. Смотрите эту статью для более подробной информации. Вот как выглядит этот класс:
[CompilerGenerated]
private sealed class <GetEnumerable>d__0 : IEnumerable<Animal>, IEnumerable, IEnumerator<Animal>, IEnumerator, IDisposable
{
private Animal <>2__current;
private int <>1__state;
private int <>l__initialThreadId;
public FarmCollection <>4__this;
public Animal <a>5__1;
public Animal[] <>7__wrap3;
public int <>7__wrap4;
Animal IEnumerator<Animal>.Current
{
[DebuggerHidden]
get
{
return this.<>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return this.<>2__current;
}
}
[DebuggerHidden]
IEnumerator<Animal> IEnumerable<Animal>.GetEnumerator()
{
FarmCollection.<GetEnumerable>d__0 <GetEnumerable>d__;
if (Environment.CurrentManagedThreadId == this.<>l__initialThreadId && this.<>1__state == -2)
{
this.<>1__state = 0;
<GetEnumerable>d__ = this;
}
else
{
<GetEnumerable>d__ = new FarmCollection.<GetEnumerable>d__0(0);
<GetEnumerable>d__.<>4__this = this.<>4__this;
}
return <GetEnumerable>d__;
}
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator()
{
return this.System.Collections.Generic.IEnumerable<ConsoleApplication479.Animal>.GetEnumerator();
}
bool IEnumerator.MoveNext()
{
bool result;
try
{
switch (this.<>1__state)
{
case 0:
this.<>1__state = -1;
this.<>1__state = 1;
this.<>7__wrap3 = this.<>4__this._farm;
this.<>7__wrap4 = 0;
goto IL_8D;
case 2:
this.<>1__state = 1;
this.<>7__wrap4++;
goto IL_8D;
}
goto IL_A9;
IL_8D:
if (this.<>7__wrap4 < this.<>7__wrap3.Length)
{
this.<a>5__1 = this.<>7__wrap3[this.<>7__wrap4];
this.<>2__current = this.<a>5__1;
this.<>1__state = 2;
result = true;
return result;
}
this.<>m__Finally2();
IL_A9:
result = false;
}
catch
{
this.System.IDisposable.Dispose();
throw;
}
return result;
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
void IDisposable.Dispose()
{
switch (this.<>1__state)
{
case 1:
break;
case 2:
break;
default:
return;
}
this.<>m__Finally2();
}
[DebuggerHidden]
public <GetEnumerable>d__0(int <>1__state)
{
this.<>1__state = <>1__state;
this.<>l__initialThreadId = Environment.CurrentManagedThreadId;
}
private void <>m__Finally2()
{
this.<>1__state = -1;
}
}
Так что в вашем коде rator
Переменная относится к объекту этого типа. Поскольку этот тип реализует IEnumerator<Animal>
, это объясняет, почему актерский состав не провалился.
Теперь к вашему второму вопросу. Обратите внимание, что GetEnumerable
Метод, сгенерированный компилятором, создает FarmCollection.<GetEnumerable>d__0
экземпляр и дает значение -2
конструктору. Это хранится в <>1__state
переменная. Теперь взгляните на MoveNext()
метод. Он имеет оператор переключения над <>1__state
переменная. Если значение такой переменной не равно 0 или 2, метод гарантированно вернет false
Это означает, что никакие значения не будут возвращены из перечисления.
Обратите внимание, как GetEnumerator()
метод в этом классе изменяет состояние на 0 и возвращает тот же экземпляр (в некоторых случаях он возвращает новый экземпляр класса с состоянием 0), что делает MoveNext
метод работы.
Так что в основном MoveNext
метод не будет работать без выполнения GetEnumerator()
,
Если вы посмотрите на обезвоженный код, вы можете увидеть, что с помощью yield return
создаст внутренний класс, который реализует оба IEnumerable<T>
а также IEnumerator<T>
, Вот почему актерский состав действителен.
Важная строка в методе GetEnumerable():
Возвращается new FarmCollection.<GetEnumerable>d__1(-2);
Итак, начальное состояние - -2, состояние "пока никто не запросил, а перечислитель все еще". Если вы позвоните GetEnumerator()
он установит свое состояние на 0, "начало состояния перечисления".
Но так как ты не звонишь GetEnumerator()
, его состояние останется -2 и, следовательно, когда MoveNext()
проверяет состояние, оно увидит -2 и вернет false.