2012-02-16 4 views
2

これは、クラスにIEnumerableを実装する際に私が遭遇した面白いエラーです。それは "変更された閉鎖へのアクセス"の問題に似ているように見えますが、私はそれを修正する方法を失っています。ここでIEnumerable <T>を実装しているときに、ポインタの誤指示が確認されました

が問題を示していますシンプルな例です:あなたはDump()の文から見ることができるように

void Main() 
{ 
    var nodeCollection = new NodeCollection(); 
    nodeCollection.MyItems = new List<string>() { "a", "b", "c" }; 

    foreach (var node in nodeCollection) 
    { 
     node.Dump(); 
    } 
} 

public class NodeCollection : IEnumerable<Node> 
{ 
    public List<string> MyItems; 

    public IEnumerator<Node> GetEnumerator() 
    { 
     // This isn't necessary, but it should prove that it's not an "access to modified closure" issue. 
     var items = MyItems; 
     for (var i = 0; i < 3; i++) 
     { 
      var node = new Node(); 

      // I want the node to contains the items in MyItems. 
      node.Items = items; 

      // Plus an additional item. Note that I am adding the item to the node, NOT to MyItems. 
      node.Items.Add(string.Format("iteration: {0}", i)); 

      yield return node; 
     } 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 
} 

public class Node 
{ 
    public List<string> Items; 
} 

、私はLINQPadでこれを実行しているんだけど、問題は、任意のIDEで自分自身を紹介します。

私はスニペットを実行すると、私は次のような出力が得られます。

Snippet Result

私は新たにインスタンス化されたNodeItemsに項目を追加していますので、私はアイテムがMyItemsに追加されることを期待しないだろうしかし、これは明らかに何が起こっているかです。

NodeItemsがをNodeCollectionとしているようです。

誰も教えてもらえます:

  • これが起こっているのはなぜ?
  • どのように起こらないようにするには?

答えて

2

それぞれの反復で新しいノードを作成していますが、各ノードのItemsプロパティに同じitemsインスタンスを設定しています。次に、アイテムコレクションに格納されているアイテムインスタンス(常に同じインスタンス)に反復文字列を追加します。その結果、後続の各ノードには「反復」エントリがますます多くなります。すべてのノードを保持していれば、それらのノードの値はまったく同じItemsという値になります。

ここでの基本的な誤解は、Node(node.Items = items;)のItemsプロパティを設定するとitemsリストがノードにコピーされると仮定していたと思います。実際には、node.Itemsは、itemsという既存のリストを指すように設定されています。

// This same instance of items is being reused each time 
    var items = MyItems; 
    for (var i = 0; i < 3; i++) 
    { 
     var node = new Node(); 

     // I want the node to contains the items in MyItems. 
     // Assuming node.Items is a List<String> 
     node.Items = new List<String>(); 
     node.Items.AddRange(items); 
     node.Items.Add(string.Format("iteration: {0}", i)); 

     yield return node; 
    } 
+0

これは私がSOを愛する理由です。私にとって完全に困惑していることは他人には明らかです。私はまだ行動をかなり理解していない、私に数分を与える... – Anders

+0

コメントの真ん中に私の更新を見て、 "私はここで基本的な誤解と思う..." –

+0

それは意味がある、何らかの理由で既存のものを指すのではなく、リストをコピーしていると思っていました。 – Anders

2

node.Items = items;セットnode.Itemsitemsリストを参照すること:

これはあなたが間違っていたアイデアを与える必要があります。リストは1つだけあり、複数の参照があります。

あなたが望むのは、各ノードに別々のリストを持ち、itemsの要素をそのリストにコピーすることです。これを行うには、itemsのすべての要素を含む新しいリストを作成します。

node.Items = new List<string>(items); 
+0

私は理解しています、そして今それは明らかに明白です... – Anders

+0

恐ろしい名前btw。 – Anders

0

次の場合は、次のようになります。 var item = MyItems; MyItemsへの参照を作成し、それを変数itemに格納するだけです。次に、あなたがするとき: node.Items = items; 同じ参照を取得し、node.Itemsに格納するだけです。ノードが必要な場合。新しいリスト(別のメモリ位置を指す)である項目は、それを再び初期化します。 node.Items = new List();

関連する問題