【C#】クラスをDeep Copyする

ローカルに保存したデータクラスを変更した場合、その参照先のデータも変更されます。 例えば、Saveボタンがあり、ボタンを押したときだけデータクラスを変更したい場合には、変更前の値をどこかに保持しなくてはいけません。

単純に次のように別の変数を用意しても、クラスが参照型のため、currentInfoが変わった時、prevInfoも変わってしまいます。

// HogeInfoはデータクラス
HogeInfo prevInfo = currentInfo

この場合、クラスをDeepCopyをすれば問題が解決できます。

DeepCopy

DeepCopyを行うときには Activator.CreateInstance()を使い、新たにクラスインスタンスを作成します。

次に、Copyをする対象の変数を取得し、コピー元のあたりを使って上書きます。

public static HogeInfo DeepCopy(HogeInfo self)
{
    // インスタンスを作成
    var instance = Activator.CreateInstance(typeof(HogeInfo), true) as HogeInfo;
    var type = self.GetType();

    // データクラスはプロパティで構成されているのでプロパティを取得しています。
    var properties = type.GetProperties();
    foreach (var property in properties)
    {
        property.SetValue(instance, property.GetValue(self));
    }
    return instance;
}

Activator.CreateInstance

var instance = Activator.CreateInstance(typeof(HogeInfo), true) as HogeInfo;

docs.microsoft.com

第2引数ではコンストラクタを公開しているかどうかのフラグです。

今回していない場合はtrueを選択します。 また別途パラメーターがあるコンストラクタを定義している場合には、パラメーターなしのコンストラクタを用意する必要があります。

public HogeInfo()
{
}

置き換え処理

今回の例ではプロパティの値を置き換えていました。

var properties = type.GetProperties();
foreach (var property in properties)
{
     property.SetValue(instance, property.GetValue(self));
}

プロパティ以外にも取得する方法があります。 例えばInstanceされたprivate変数を取得する場合は次のような記述ができます。

// private変数の場合
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
foreach (var field in fields)
{

}
BindingFlags

BindingFlagsはこちらを参考に。 docs.microsoft.com

使用例

DeepCopyは次の用に利用します。

HogeInfo prevInfo = HogeInfo.DeepCopy(currentInfo);

これで変更前の値を保持したローカルクラスを作成できます。