C# タプルのリストを作成したらリストの要素が全て同じ値になった

こんにちは、kisseです。

今回もC#の話題です。




問題

タプルのリストを作ってたのですが、その全ての値が同じになってしまってました。
書いたコードはこんな感じ。


// 冗長な書き方してるのは、わざと半分ごめん半分。
SomeClass someObj1 = new SomeClass();
SomeClass someObj2 = new SomeClass();

// リストを作成
List<Tuple<SomeClass, SomeClass>> tupleList = new List<Tuple<SomeClass, SomeClass>>();

for (int i = 0; i < 5; i++) 
{
  // 適当な値を代入
  someObj1.val = i;
  someObj2.val = i * i;

  // タプルを作る
  // Tuple.Create()とかあるが今回はnew使ってたのでこっち。どうでもいい。
  Tuple<SomeClass, SomeClass>> tuple = new Tuple<SomeClass, SomeClass>(someObj1, SomeObj2);

  // リストに追加
  tupleList.Add(tuple);
}

こんな感じ。
forループの中でタプルを作ってリストに追加してるだけですね。

しかしリストの要素が全部同じ値になってしまいました。

原因と解決

毎回毎回あたらしいタプルのインスタンスを生成してるので、リストの要素は違うタプルを指してるはずです。
それなのに、なぜ同じ値を示してしまったのか。

それは、全てのタプルが同じSomeClassのインスタンスを指していたからです。
SomeClassのインスタンスはループの中で再生成しておらず、冒頭で生成したインスタンスの値を変えてるだけでした。

なので、それらを要素に持つタプルをいくら生成してもその値は同じになってしまった訳です。

原因がわかったところで書き直しましょう。


// ここは変数の宣言だけにしよう
SomeClass someObj1;
SomeClass someObj2;

// リストを作成
List<Tuple<SomeClass, SomeClass>> tupleList = new List<Tuple<SomeClass, SomeClass>>();

for (int i = 0; i < 5; i++) 
{
  // ちゃんとループの中でインスタンスの再生成を行おう。
  someObj1 = new SomeClass();
  someObj2 = new SomeClass();

  // 適当な値を代入
  someObj1.val = i;
  someObj2.val = i * i;

  // これで、ちゃんとタプルの要素が異なるSomeClassのインスタンスを指すようになった。
  Tuple<SomeClass, SomeClass>> tuple = new Tuple<SomeClass, SomeClass>(someObj1, SomeObj2);

  // リストに追加
  tupleList.Add(tuple);
}

こんな感じでできました。

わりと初歩的なミスで悲しみが深い。。




おわり

参照の管理には気をつけような!!!

最後まで読んで頂きありがとうございます!

あわせて読みたい