3

比較を通じて、異なるタイプの2つの列挙可能オブジェクトを反復処理するロジックを作成しているときに、次のことがわかりました。

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:次に移動する必要がありますか?False、yesの場合、値:0

なんで?

4

3 に答える 3

7

IEnumerator<T>.Currentプロパティに関するMSDNドキュメントによると:

Currentis undefined [when]: return への最後の呼び出しMoveNextfalse、コレクションの終わりを示します。

Currentこれは、列挙子の基になる実装が、一度MoveNext返された任意の値を自由に返すことを意味しますfalse。0、6、-1、2147483647、またはランダムに選択された値です。とにかく、あなたはそれを使わないことが期待されています。

于 2012-12-04T12:40:54.897 に答える
2

List<T>クラスで使用される列挙子で何が起こるかを見てみましょう(これはですList<int>.Enumerator)。実際、 msdnは現在の値は未定義になると言っていますが、Framework4.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)値(0int用)を返します。

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

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

生成された列挙子はどうですか?yield returnC#は、各ステートメントの状態を持つ列挙子クラスを生成します。次の状態に移動すると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 に答える
2

継承された IEnumerator の実現次第です。リストには、これを説明する列挙子の実現があります。

[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 に答える