从 IEnumerable 转换为 IEnumerator
Casting from IEnumerable to 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> enumerator = (IEnumerator<Animal>)farm.GetEnumerable();
while (enumerator.MoveNext())
{
Animal a = (Animal)enumerator.Current;
Console.WriteLine(a.AnimalType);
}
}
}
第一个问题:为什么我没有输出,Main 只是 returns?
第二个问题:为什么从IEnumerable<Animal>
到IEnumerator<Animal>
的转换没有出现编译错误?
如果您查看 de-sugared code,您会发现使用 yield return
将创建一个同时实现 IEnumerable<T>
和 IEnumerator<T>
的内部 class。这就是强制转换有效的原因。
重要的一行在 GetEnumerable() 方法中:
它 returns new FarmCollection.<GetEnumerable>d__1(-2);
所以,初始状态是-2,即"No-one-has-requested-an-Enumerator-yet state"。如果您调用 GetEnumerator()
它将其状态设置为 0,"start of enumeration state".
但是由于您没有调用 GetEnumerator()
,它的状态将保持为 -2,因此,当 MoveNext()
检查状态时,它会看到 -2 和 return false。
下面是您的 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
也是由编译器生成的。有关详细信息,请参阅 this article。这是 class 的样子:
[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;
}
}
所以在你的代码中,enumerator
变量引用了这个类型的对象。由于此类型实现了 IEnumerator<Animal>
,这就解释了为什么转换没有失败。
现在,回答你的第二个问题。请注意,编译器生成的 GetEnumerable
方法构造 FarmCollection.<GetEnumerable>d__0
实例并将值 -2
赋给构造函数。这存储在 <>1__state
变量中。现在,看看 MoveNext()
方法。它在 <>1__state
变量上有一个 switch 语句。如果此类变量的值不是 0
或 2
,则该方法保证为 return false
,这意味着不会 return 编辑任何值枚举。
注意这个 class 中的 GetEnumerator()
方法如何将状态更改为 0 并且 return 是同一个实例(在某些情况下,它 return 是一个新实例状态为 0 的 class),这将使 MoveNext
方法起作用。
所以基本上 MoveNext
方法在不执行 GetEnumerator()
.
的情况下将无法工作
我正在玩 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> enumerator = (IEnumerator<Animal>)farm.GetEnumerable();
while (enumerator.MoveNext())
{
Animal a = (Animal)enumerator.Current;
Console.WriteLine(a.AnimalType);
}
}
}
第一个问题:为什么我没有输出,Main 只是 returns?
第二个问题:为什么从IEnumerable<Animal>
到IEnumerator<Animal>
的转换没有出现编译错误?
如果您查看 de-sugared code,您会发现使用 yield return
将创建一个同时实现 IEnumerable<T>
和 IEnumerator<T>
的内部 class。这就是强制转换有效的原因。
重要的一行在 GetEnumerable() 方法中:
它 returns new FarmCollection.<GetEnumerable>d__1(-2);
所以,初始状态是-2,即"No-one-has-requested-an-Enumerator-yet state"。如果您调用 GetEnumerator()
它将其状态设置为 0,"start of enumeration state".
但是由于您没有调用 GetEnumerator()
,它的状态将保持为 -2,因此,当 MoveNext()
检查状态时,它会看到 -2 和 return false。
下面是您的 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
也是由编译器生成的。有关详细信息,请参阅 this article。这是 class 的样子:
[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;
}
}
所以在你的代码中,enumerator
变量引用了这个类型的对象。由于此类型实现了 IEnumerator<Animal>
,这就解释了为什么转换没有失败。
现在,回答你的第二个问题。请注意,编译器生成的 GetEnumerable
方法构造 FarmCollection.<GetEnumerable>d__0
实例并将值 -2
赋给构造函数。这存储在 <>1__state
变量中。现在,看看 MoveNext()
方法。它在 <>1__state
变量上有一个 switch 语句。如果此类变量的值不是 0
或 2
,则该方法保证为 return false
,这意味着不会 return 编辑任何值枚举。
注意这个 class 中的 GetEnumerator()
方法如何将状态更改为 0 并且 return 是同一个实例(在某些情况下,它 return 是一个新实例状态为 0 的 class),这将使 MoveNext
方法起作用。
所以基本上 MoveNext
方法在不执行 GetEnumerator()
.