从 IEnumerable 转换为 IEnumerator

问题描述 投票:0回答:2

我在玩

IEnumerable/<T>
IEnumerator/<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只是返回?
第二个问题:为什么从

IEnumerable<Animal>
IEnumerator<Animal>
的转换没有出现编译错误?

c# ienumerable ienumerator
2个回答
3
投票

这是你的

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;
    }
}

因此在您的代码中,

enumerator
变量指的是这种类型的对象。由于这种类型实现了
IEnumerator<Animal>
,这就解释了为什么转换没有失败。

现在,第二个问题。请注意,编译器生成的

GetEnumerable
方法构造了
FarmCollection.<GetEnumerable>d__0
实例,并将值
-2
赋给了构造函数。这存储在
<>1__state
变量中。现在,看看
MoveNext()
方法。它在
<>1__state
变量上有一个 switch 语句。如果此类变量的值不是
0
2
,则该方法保证返回
false
,这意味着不会从枚举中返回任何值。

注意此类中的

GetEnumerator()
方法如何将状态更改为0并返回相同的实例(在某些情况下,它返回状态为0的类的新实例)这将使
MoveNext
方法起作用。

所以基本上

MoveNext
方法在不执行
GetEnumerator()
的情况下将不起作用。


2
投票

如果您查看脱糖代码,您会发现使用

yield return
将创建一个同时实现
IEnumerable<T>
IEnumerator<T>
的内部类。这就是演员阵容有效的原因。

重要的一行在方法 GetEnumerable() 中:
它返回

new FarmCollection.<GetEnumerable>d__1(-2);

因此,初始状态为 -2,即“尚未请求枚举器状态”。如果你调用

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

© www.soinside.com 2019 - 2024. All rights reserved.