将IEnumerable强制转换为IEnumerator
将IEnumerable强制转换为IEnumerator
我正在使用 IEnumerable/
和 IEnumerator/
。在我的一个试验中,我尝试使用强制类型转换将类型为 IEnumerable
的返回值赋值给 IEnumerator
的值,然后尝试执行 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 GetEnumerable() { foreach (Animal a in _farm) yield return a; } } class Test { public static void Main() { FarmCollection farm = new FarCollection(); IEnumerator enumerator = (IEnumerator)farm.GetEnumerable(); while (enumerator.MoveNext()) { Animal a = (Animal)enumerator.Current; Console.WriteLine(a.AnimalType); } } }
第一个问题:为什么我没有输出,而主函数只是返回了?
第二个问题:为什么从 IEnumerable
强制转换到 IEnumerator
没有出现编译错误?
如果您查看去糖代码,您会发现使用yield return
将创建一个实现IEnumerable
和IEnumerator
的内部类。这就是为什么转换是有效的原因。
重要的代码在GetEnumerable()
方法中:
它返回new FarmCollection.
因此,初始状态为-2,即“没有请求枚举器状态”。如果调用GetEnumerator()
,它将将其状态设置为0,即“枚举开始状态”。但由于您没有调用GetEnumerator()
,它的状态将保持为-2,因此,MoveNext()
检查状态时会看到-2并返回false。
以下是反编译后的 FarmCollection.GetEnumerable
方法代码:
public IEnumerableGetEnumerable() { FarmCollection. d__0 d__ = new FarmCollection. d__0(-2); d__.<>4__this = this; return d__; }
编译器还会生成一个类型为 FarmCollection.
的类。有关详细信息,请参见 此文章。以下是该类的代码:
[CompilerGenerated] private sealed classd__0 : IEnumerable , IEnumerable, IEnumerator , IEnumerator, IDisposable { private Animal <>2__current; private int <>1__state; private int <>l__initialThreadId; public FarmCollection <>4__this; public Animal 5__1; public Animal[] <>7__wrap3; public int <>7__wrap4; Animal IEnumerator .Current { [DebuggerHidden] get { return this.<>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return this.<>2__current; } } [DebuggerHidden] IEnumerator IEnumerable .GetEnumerator() { FarmCollection. d__0 d__; if (Environment.CurrentManagedThreadId == this.<>l__initialThreadId && this.<>1__state == -2) { this.<>1__state = 0; d__ = this; } else { d__ = new FarmCollection. d__0(0); d__.<>4__this = this.<>4__this; } return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return this.System.Collections.Generic.IEnumerable .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.5__1 = this.<>7__wrap3[this.<>7__wrap4]; this.<>2__current = this.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 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
,这就解释了为什么转换未失败。
现在,对于您的第二个问题。请注意,编译器生成的 GetEnumerable
方法构造了 FarmCollection.
实例,并将值 -2
赋给构造函数。这存储在 <>1__state
变量中。现在请查看 MoveNext()
方法。它针对 <>1__state
变量进行了 switch 语句。如果此变量的值不是 0
或 2
,那么该方法保证将返回 false
,这意味着不会从枚举中返回任何值。
请注意,此类中的 GetEnumerator()
方法会将状态更改为 0,并返回相同的实例(在某些情况下,它会返回状态为 0 的新类实例),这使得 MoveNext
方法能够工作。
因此,基本上在执行 GetEnumerator()
之前,MoveNext
方法是不起作用的。