4

えーと、これをどう表現するかはよくわかりませんが..

クラスの3つのインスタンスを含むyieldreturnを使用して作成されたIEnumerableがある場合、.First()を呼び出すと最初のインスタンスの「コピー」が返されるように見えるのはなぜですか?

次のコードを参照してください。

public class Thing
    {
        public bool Updated { get; set; }

        public string Name { get; private set; }

        public Thing(string name)
        {
            Name = name;
        }

        public override string ToString()
        {
            return string.Format("{0} updated {1} {2}", Name, Updated, GetHashCode());
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("IEnumerable<Thing>");
            var enumerableThings = GetThings();
            var firstThing = enumerableThings.First();
            firstThing.Updated = true;
            Console.WriteLine("Updated {0}", firstThing);
            foreach (var t in enumerableThings)
                Console.WriteLine(t);

            Console.WriteLine("IList<Thing>");
            var thingList = GetThings().ToList();
            var thing1 = thingList.First();
            thing1.Updated = true;
            Console.WriteLine("Updated {0}", thing1);
            foreach (var t in thingList)
                Console.WriteLine(t);

            Console.ReadLine();
        }

        private static IEnumerable<Thing> GetThings()
        {
            for (int i = 1; i <= 3; i++)
            {
                yield return new Thing(string.Format("thing {0}", i));
            }
        }
    }
}

これを実行すると、次の出力が生成されます。

IEnumerable<Thing>
Updated thing 1 updated True 37121646
thing 1 updated False 45592480
thing 2 updated False 57352375
thing 3 updated False 2637164
IList<Thing>
Updated thing 1 updated True 41014879
thing 1 updated True 41014879
thing 2 updated False 3888474
thing 3 updated False 25209742

しかし、IListとIEnmerableは同じように動作し、次のように出力されると思います...

IEnumerable<Thing>
Updated thing 1 updated True 45592480
thing 1 updated False 45592480
thing 2 updated False 57352375
thing 3 updated False 2637164
IList<Thing>
Updated thing 1 updated True 41014879
thing 1 updated True 41014879
thing 2 updated False 3888474
thing 3 updated False 25209742

何が足りないの?

4

4 に答える 4

3

このメソッドは実際のコレクションを返しGetThingsません。コレクションを「調理」する方法の「レシピ」を返し、反復を要求した場合にのみ「調理」されます。それがの魔法ですyield

したがって、ループを呼び出すたび.First()にループが実行され、実際に新しいインスタンスが作成されます。

于 2011-10-11T16:05:03.517 に答える
1

'yield return'によって構築されたIEnumerableは、列挙された場合にのみ値を生成し、最初のケースでは2回列挙します。2回目に列挙するときに、実際には完全に別個のモノのセットを作成しています。

Yield returnは基本的に、ステートマシンを生成するコードのショートカットであり、ステートマシンは、列挙されると、コードを進行して列挙された結果を生成します。結果自体は、リストに入れるなど、何かを行うまでどこにも保存されません。

于 2011-10-11T16:04:41.983 に答える
0

イテレータ(を使用するすべてのメソッドyield return)は、反復するときに遅延評価されます。つまり、メソッド本体は、呼び出したときに実行されません。IEnumerable<T>呼び出しなどを介して、結果を列挙したときにのみ実行されますforeach。そして、それはあなたがするたびに実行されますforeach

(要素を取得する唯一の方法であるため).First()を列挙するIEnumerable<T>必要があるため、メソッド本体はを呼び出すたびに再実行されます.First()

.ToList()通常の解決策は、またはを呼び出すことにより、準備ができた時点でイテレータを強制的に実行することです.ToArray()。これにより、または配列が得られますが、List<T>反復しても変更されなくなります。

于 2011-10-11T16:05:44.717 に答える
0

IEnumerable実装(GetThings)は、反復するたびに新しいアイテムを返します。したがって、IEnumerableのforeachで、各Thingが新しく作成されます。ToListをIEnumerableに追加すると、IEnumerableによって作成された「もの」がリストに保存されるという意味で、リストにはIEnumerableから生成された各アイテムの「コピー」が実際に含まれます。「things」のリストに対する後続の反復では、常に同じ「things」のセットが生成されます。IEnumerableに対する後続の反復では、常に新しい「もの」のセットが生成されます。

于 2011-10-11T16:07:25.310 に答える