バグの例
まずバグの例を見てください。
1行分の文字列を保持する Data クラスを使用して、
ファイルの各 1 行を Data クラスのリストを使って処理をする
という簡単なプログラム例です。
Program.cs
プログラムのエントリポイントです。昔で言うところの Main 処理に該当します。
using Sample1; SampleClass sampleClass = new(); sampleClass.SampleMethod();
Data.cs
各行の内容を保持する Data クラスです。
namespace Sample1 { internal class Data { public int Row { get; set; } public string Line { get; set; } public Data() { Row = 0; Line = string.Empty; } public Data(int row, string line) { Row = row; Line = line; } } }
SampleClass.cs
実際の主処理を行う SampleClass です。
namespace Sample1 { internal class SampleClass { public void SampleMethod() { List<Data> list = new(); Data data = new(); int row = 0; foreach (string line in File.ReadLines(@"input.txt")) { row++; data.Row = row; data.Line = line; list.Add(data); } foreach (Data _data in list) { Console.WriteLine("{0}:{1}", _data.Row, _data.Line); } } } }
input.txt
このプログラムへ入力する input.txt です。
Visual Studio を使用する場合は、プロジェクトにテキストファイルを追加して、
以下のように適当なデータを 3 行ぐらい用意して保存してください。
さらに、プロパティで [出力ディレクトリにコピー] を [常にコピーする] に設定してください。
あいうえお かきくけこ さしすせそ
このプログラムを実行するとコンソールに
3:さしすせそ 3:さしすせそ 3:さしすせそ
と最後の行を 3 回表示します。
これは期待していた出力内容ではありません。
バグの原因
では、なぜバグになってしまったのでしょうか。
それは、オブジェクトを保持する変数への代入は、「参照渡し」だからです。
例示したプログラムでは Data クラスはループ外で 1 度しか new しておらず、
ループ内で list に追加しています。プログラムが保持しているオブジェクトを図示するとこうです。
「参照渡し」なので list には同じ Data オブジェクトの参照が Add されます。
最後に Data オブジェクトに格納したのは、3 行目のデータですので、
3 行目のデータを毎回表示するわけです。
バグ修正
まずはバグ修正したものを図示しておきます。
このように各行を処理する度に Data オブジェクトを用意し、
そのオブジェクトへの参照を list に追加しなければいけません。
SampleClass.cs
修正した SampleClass です。
namespace Sample1 { internal class SampleClass { public void SampleMethod() { List<Data> list = new(); int row = 0; foreach (string line in File.ReadLines(@"input.txt")) { row++; Data data = new(); data.Row = row; data.Line = line; list.Add(data); } foreach (Data _data in list) { Console.WriteLine("{0}:{1}", _data.Row, _data.Line); } } } }
このようにループ内で Data オブジェクトを作成します。
もう一度ビルドして実行すると、
1:あいうえお 2:かきくけこ 3:さしすせそ
今度は正しい結果が得られました。
では、今回のまとめです。
まとめ
オブジェクトを格納する変数は、オブジェクトへの参照を保持しているだけです。
オブジェクトの実データを保持しているわけではありません。
課題
例示したサンプルプログラムはまだ改良の余地を残しています。
どこをどんな風に直すのが適切なのか考えてみてください。
(Data クラスのコンストラクタにヒントがあります。)