2011-10-11 10 views
4

のEr、かなり確実ではないかとフレーズこれをしかし、列挙内の最初の項目のコピーを返すように見える..はなぜ考えるEnumerable.First()

を呼び出すんIEnumerableを3つのインスタンスを含む、歩留まりのリターンを使用して作成.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 

私は何をしないのです!

答えて

3

方法GetThingsではないリターンリアルコレクション。それは、 "レシピ"をどのように "料理"するかを返します。そして、あなたがそれを繰り返すように頼んだときにのみ、 "料理"されます。それはyieldの魔法です。

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

+1

Dan Bryantの回答は、正確な用語で技術的な部分をよりよくカバーしています。:) –

+0

もちろん、まあ、ありがとう – sackoverflow

1

'yield return'で作成されたIEnumerableは、列挙されるときに値を生成し、最初のケースでは2回列挙します。 2回目に列挙すると、実際には完全に別のものが作成されています。

収穫の戻り値は、基本的に状態マシンを生成するコードのショートカットです。状態マシンは列挙されると列挙された結果を得るためにコードを進めます。結果自体は、リストに入れるなど、何かを行うまではどこにも保存されません。

0

反復子(yield returnを使用する任意の方法)は、反復処理するときに遅延評価されます。これは、メソッド本体が呼び出されたときに実行されないことを意味します。foreachなどの呼び出しによって、結果のIEnumerable<T>を列挙するときにのみ実行されます。そして、それぞれを実行します。あなたはforeachです。

.First()が(それはそれのうちの要素を取得する唯一の方法だと)IEnumerable<T>を列挙しているので、あなたのメソッド本体が再実行あなたが.First()を呼び出すたびに取得します。

普通の解決策は、準備が整ったらある時点でイテレータを強制的に実行することです。.ToList()または.ToArray()を呼び出します。これにより、List<T>または配列が返されます。これは、繰り返し処理すると変更されなくなります。

0

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

関連する問題