将IEnumerable强制转换为IEnumerator

30 浏览
0 Comments

将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 没有出现编译错误?

admin 更改状态以发布 2023年5月21日
0
0 Comments

如果您查看去糖代码,您会发现使用yield return将创建一个实现IEnumerableIEnumerator的内部类。这就是为什么转换是有效的原因。

重要的代码在GetEnumerable()方法中:

它返回new FarmCollection.d__1(-2);

因此,初始状态为-2,即“没有请求枚举器状态”。如果调用GetEnumerator(),它将将其状态设置为0,即“枚举开始状态”。
但由于您没有调用GetEnumerator(),它的状态将保持为-2,因此,MoveNext()检查状态时会看到-2并返回false。

0
0 Comments

以下是反编译后的 FarmCollection.GetEnumerable 方法代码:

public IEnumerable GetEnumerable()
{
    FarmCollection.d__0 d__ =
        new FarmCollection.d__0(-2);
    d__.<>4__this = this;
    return d__;
}

编译器还会生成一个类型为 FarmCollection.d__0 的类。有关详细信息,请参见 此文章。以下是该类的代码:

[CompilerGenerated]
private sealed class d__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.d__0 实例,并将值 -2 赋给构造函数。这存储在 <>1__state 变量中。现在请查看 MoveNext() 方法。它针对 <>1__state 变量进行了 switch 语句。如果此变量的值不是 02,那么该方法保证将返回 false,这意味着不会从枚举中返回任何值。

请注意,此类中的 GetEnumerator() 方法会将状态更改为 0,并返回相同的实例(在某些情况下,它会返回状态为 0 的新类实例),这使得 MoveNext 方法能够工作。

因此,基本上在执行 GetEnumerator() 之前,MoveNext 方法是不起作用的。

0