【C#】クラス型変数をコピーする方法
C#で変数をコピーしたとき、
自分の想定では値をコピーしたものだと思っていたのに
いざ結果を見てみると、
コピーした変数の値を変更したら
コピー元の変数の値も変わっていた!
という経験が誰しもあると思います。
私だけじゃないと思います・・・たぶん
という事で、今回は参照型変数についての話と
参照型になるクラスの値をコピーする方法のご紹介です
参照型とは?intやstringとは違うの?
参照型と値型の違いについては別記事で語ってます。
以下のリンクをご確認ください
apuridasuo.hatenablog.com
参照型の変数を普通にコピーした時
では実際に参照型となる自作クラスをコピーしてみます
/******************************************* コピーする自作クラス ********************************************/ public class TestClass { public int _Id; public string _Name; public string[] _Array01; //========================= // 通常のコンストラクタ //========================= public TestClass() { _Id = 0; _Name = ""; _Array01 = new string[2] { "", "" }; } } /******************************************* コピー処理を行う関数 ********************************************/ private void Main() { // コピーする変数を作成 TestClass TestClassBef = new TestClass(); TestClassBef._Id = 1; TestClassBef._Name = "コピー前"; TestClassBef._Array01[0] = "ま"; TestClassBef._Array01[1] = "え"; // コピーしてコピー後の変数の値を変えてみる TestClass TestClassCopy = TestClassBef; TestClassCopy._Id = 2; TestClassCopy._Name = "コピー後"; TestClassCopy._Array01[0] = "あ"; TestClassCopy._Array01[1] = "と"; }
こちらが自作クラス「TestClass 」の型の変数「TestClassBef 」を作成後、
別の変数「TestClassCopy 」にコピーして、値を変更するソースです。
処理後の変数の中身をメッセージ出力した結果が以下になります
// コピー前の変数の中身 TestClassBef._Id = 2 TestClassBef._Name = "コピー後" TestClassBef._Array01[0] = "あ" TestClassBef._Array01[1] = "と" // コピー後の変数の中身 TestClassCopy._Id = 2 TestClassCopy._Name = "コピー後" TestClassCopy._Array01[0] = "あ" TestClassCopy._Array01[1] = "と"
コピー前の変数も値が変わってしまっていますね。
これが参照型変数の特徴なんです。
クラス型の変数を別変数としてコピーする方法
コピー後の変数を変更しても、
コピー前の変数に影響を与えない方法をご紹介します。
自作クラスのコンストラクタを追加します!
/**************************** コピーしたいクラス *****************************/ public class TestClass { public int _Id; public string _Name; public string[] _Array01; //==================== // 通常のコンストラクタ //==================== public TestClass() { _Id = 0; _Name = ""; _Array01 = new string[2] { "", "" }; } //==================== // コピー用コンストラクタ //==================== public TestClass(TestClass CopyClass) { _Id = CopyClass._Id; _Name = CopyClass._Name; _Array01 = new string[2] { "", "" }; Array.Copy(CopyClass._Array01, _Array01, CopyClass._Array01.Length); } } /******************************************* 呼び出す関数 ********************************************/ private void Main() { TestClass TestClassBef = new TestClass(); TestClassBef._Id = 1; TestClassBef._Name = "コピー前"; TestClassBef._Array01[0] = "ま"; TestClassBef._Array01[1] = "え"; // コンストラクタを使って変数をコピーする TestClass TestClassCopy = new TestClass(TestClassBef); TestClassCopy._Id = 2; TestClassCopy._Name = "コピー後"; TestClassCopy._Array01[0] = "あ"; TestClassCopy._Array01[1] = "と"; }
以上がソースになります。
通常コピーで書いたソースと見比べてみてください。
まず自作クラスの方にコンストラクタを追加しています。
※1つのクラスに、引数を変えれば複数のコンストラクタを作成できます
自分のクラス型を引数にしたコンストラクタです。
このコンストラクタの中で、
コピー元の変数の中身を取得する処理を記入しましょう!
そして、コピーする変数「TestClassCopy」作成時の記述も変わってます
「new TestClass(TestClassBef);」と定義することで、
コピー前と違ったアドレスを持った空の変数ができます。
そして、作成したコンストラクタによってコピー前の中身を持ってきます。
ソースの実行結果が以下になります
// コピー前の変数の中身 TestClassBef._Id = 1 TestClassBef._Name = "コピー前" TestClassBef._Array01[0] = "ま" TestClassBef._Array01[1] = "え" // コピー後の変数の中身 TestClassCopy._Id = 2 TestClassCopy._Name = "コピー後" TestClassCopy._Array01[0] = "あ" TestClassCopy._Array01[1] = "と"
ちゃんとコピー後変数の変更がコピー前の変数に影響を与えていません。
このように、
参照型の変数は手順を踏んでコピーを行わないと
簡単に想定外の不具合が起きます!
私のコピーの仕方はほんの一例でしかありません。
参照型というものは不具合に遭遇して経験を積まないと
ぱっと理解できるものじゃないと思います。
今後もバグらせまくると思いますので、
変なバグをやっちゃったらまとめたいと思います。