3

当我通过比较来迭代两个具有不同类型的可枚举时,我发现了这一点:

class Program
{
    public static IEnumerable<mm> YieldlyGet()
    {
        yield return new mm { Int = 0 };
        yield return new mm { Int = 1 };
        yield return new mm { Int = 2 };
        yield return new mm { Int = 3 };
        yield return new mm { Int = 4 };
        yield return new mm { Int = 5 };
    }

    public static IEnumerable<int> YieldlyGetInt()
    {
        yield return 0;
        yield return 1;
        yield return 2;
        yield return 3;
        yield return 4;
        yield return 5;
    }

    public static IEnumerable<int> Get() 
    {
        return new List<int> { 0, 1,2,3,4,5 };
    }

    static void Main(string[] args) 
    {
        var yieldr = YieldlyGet().GetEnumerator();
        var yieldv = YieldlyGetInt().GetEnumerator();

        var list = Get().GetEnumerator();

        int i = -1;
        Console.WriteLine("For the current index: {0}", ++i);
        Console.WriteLine("y-r: Should I move next? {0}, if yes, value: {1}", yieldr.MoveNext(), yieldr.Current != null ? yieldr.Current.Int : 0);
        Console.WriteLine("y-v: Should I move next? {0}, if yes, value: {1}", yieldv.MoveNext(), yieldv.Current);
        Console.WriteLine("l: Should I move next? {0}, if yes, value: {1}", list.MoveNext(), list.Current);

        Console.WriteLine("For the current index: {0}", ++i);
        Console.WriteLine("y-r: Should I move next? {0}, if yes, value: {1}", yieldr.MoveNext(), yieldr.Current != null ? yieldr.Current.Int : 0);
        Console.WriteLine("y-v: Should I move next? {0}, if yes, value: {1}", yieldv.MoveNext(), yieldv.Current);
        Console.WriteLine("l: Should I move next? {0}, if yes, value: {1}", list.MoveNext(), list.Current);

        Console.WriteLine("For the current index: {0}", ++i);
        Console.WriteLine("y-r: Should I move next? {0}, if yes, value: {1}", yieldr.MoveNext(), yieldr.Current != null ? yieldr.Current.Int : 0);
        Console.WriteLine("y-v: Should I move next? {0}, if yes, value: {1}", yieldv.MoveNext(), yieldv.Current);
        Console.WriteLine("l: Should I move next? {0}, if yes, value: {1}", list.MoveNext(), list.Current);

        Console.WriteLine("For the current index: {0}", ++i);
        Console.WriteLine("y-r: Should I move next? {0}, if yes, value: {1}", yieldr.MoveNext(), yieldr.Current != null ? yieldr.Current.Int : 0);
        Console.WriteLine("y-v: Should I move next? {0}, if yes, value: {1}", yieldv.MoveNext(), yieldv.Current);
        Console.WriteLine("l: Should I move next? {0}, if yes, value: {1}", list.MoveNext(), list.Current);

        Console.WriteLine("For the current index: {0}", ++i);
        Console.WriteLine("y-r: Should I move next? {0}, if yes, value: {1}", yieldr.MoveNext(), yieldr.Current != null ? yieldr.Current.Int : 0);
        Console.WriteLine("y-v: Should I move next? {0}, if yes, value: {1}", yieldv.MoveNext(), yieldv.Current);
        Console.WriteLine("l: Should I move next? {0}, if yes, value: {1}", list.MoveNext(), list.Current);

        Console.WriteLine("For the current index: {0}", ++i);
        Console.WriteLine("y-r: Should I move next? {0}, if yes, value: {1}", yieldr.MoveNext(), yieldr.Current != null ? yieldr.Current.Int : 0);
        Console.WriteLine("y-v: Should I move next? {0}, if yes, value: {1}", yieldv.MoveNext(), yieldv.Current);
        Console.WriteLine("l: Should I move next? {0}, if yes, value: {1}", list.MoveNext(), list.Current);

        Console.WriteLine("For the current index: {0}", ++i);
        Console.WriteLine("y-r: Should I move next? {0}, if yes, value: {1}", yieldr.MoveNext(), yieldr.Current != null ? yieldr.Current.Int : 0);
        Console.WriteLine("y-v: Should I move next? {0}, if yes, value: {1}", yieldv.MoveNext(), yieldv.Current);
        Console.WriteLine("l: Should I move next? {0}, if yes, value: {1}", list.MoveNext(), list.Current);


        Console.ReadLine();
}

问题是,当我在最后一个位置之后,列表为我提供了默认值,而 yield 创建的迭代器继续显示最后一个值。

对于当前指数:6
年:我应该下一步移动吗?False,如果是,值:5
yv:我应该下一步移动吗?False,如果是,值:5
l:我应该下一步移动吗?,如果是,值:0

为什么?

4

3 回答 3

7

根据关于该IEnumerator<T>.Current属性的 MSDN 文档:

Currentis undefined [when]:最后一次调用MoveNext返回false,表示集合结束。

Current这意味着枚举器的底层实现可以自由地从一次MoveNext返回中返回任意值false。它可以是 0、6、-1、2147483647,或者只是随机选取的值;无论如何,您应该不会使用它。

于 2012-12-04T12:40:54.897 回答
2

这取决于继承的 IEnumerator 实现。对于列表,有描述此的 Enumerator 的实现:

[Serializable]
    public struct Enumerator : IEnumerator<T>, IDisposable, IEnumerator
    {
      private List<T> list;
      private int index;
      private int version;
      private T current;

      public T Current
      {
        get
        {
          return this.current;
        }
      }

      ....

      internal Enumerator(List<T> list)
      {
        this.list = list;
        this.index = 0;
        this.version = list._version;
        this.current = default (T); //there are default of T
      }

      ....
    }

对于产量实现是不同的。它返回最后一个值(因为电流在下一个不会改变)。

这种差异可能是由于这段代码是由不同的人在不同的时间编写的。此行为未定义,此外,如果您正确使用 IEnumerables,则不应看到此行为(因为通常人们不会显式使用 Enumerator.Next() 而是使用 foreach、linq 或通过索引直接访问(对于数组)) .

于 2012-12-04T12:43:46.040 回答
2

让我们看看枚举器中发生了什么,它被List<T>类使用(这是 a List<int>.Enumerator)。实际上msdn说当前值是未定义的,但我们可以分析 Framework 4.0 的实现。因此,当枚举器位于最后一个元素之后时,MoveNextRare将被调用:

public bool MoveNext()
{
    List<T> ts = this.list;
    if (this.version != ts._version || this.index >= ts._size)
    {
        return this.MoveNextRare();
    }
    else
    {
        this.current = ts._items[this.index];
        List<T>.Enumerator<T> enumerator = this;
        enumerator.index = enumerator.index + 1;
        return true;
    }
}

因此列表有效(未更改)此方法返回defaut(T)值(0用于 int)

private bool MoveNextRare()
{
    if (this.version != this.list._version)
        trow new InvalidOperationException();

    this.index = this.list._size + 1;
    this.current = default(T);
    return false;
}

那么生成的枚举器呢?C# 生成枚举器类,该类将具有每个yield return语句的状态。移动到下一个状态会Current为此枚举器设置值:

bool MoveNext()
{
    bool flag;
    int state = this.state;
    if (state == 0)
    {
        this.state = -1;
        this.current = 0;
        this.state = 1;
        flag = true;
    }
    else if (state == 1)
    {
        this.state = -1;
        this.current = 1;
        this.state = 2;
        flag = true;
    }
    // ...
    else if (state == 5)
    {
        this.state = -1;
        this.current = 5;
        this.state = 6;
        flag = true;
    }
    else if (state == 6)
    {
        this.state = -1;
        flag = false;
        return flag;
    }
    else
    {
        flag = false;
        return flag;
    }
    return flag;
    flag = false;
    return flag;
}

有趣的是this.current,在最后一次分配之后(状态为 时5)并没有改变。这就是为什么所有后续调用都Current将返回值,该值由上次yield return调用设置。

于 2012-12-04T13:01:18.993 回答