[C#] クラスオブジェクトのディープコピー
C#のクラスオブジェクトをコピーしたい場合がまれによくある。
特にパラメータ系のクラスオブジェクトを作成して、元のオブジェクトの値は保持したままにして
渡した先であれこれしたい場合だ。
C#のクラスは参照での扱い方がベース(でありウリである所)なので、参照先で値をいじると元の方も変わってしまう。
それを避ける為には、元のクラスオブジェクトをコピーしたものを参照先に渡す必要がある。
C++ならmemcpyして終わりな問題でこれはこれでラクだったが、C#ではそうはいかない。
代わりに、浅いコピー(シャローコピー)と深いコピー(ディープコピー)の2通りのコピー方法がある。
シャローコピーの方は簡単で、Objectクラスに元々MemberwiseCloneというメソッドがあり、
これを使って目的のクラスにキャストするだけだ。
//--------------------------------------------------------------------------------
// 自身を複製したインスタンスを返す
//--------------------------------------------------------------------------------
public HogeParam Clone()
{
return (HogeParam)MemberwiseClone();
}
ただし、MemberwiseCloneは文字通り浅いコピーであるという事に注意する必要がある。
もしクラス内に参照型の変数を持っている場合には、その参照先のクラスオブジェクトまでは複製されない。
あくまでも「参照先オブジェクトはこれです」という情報のみがコピーされるだけである。
もし参照型のメンバを持っているクラスの場合で、その参照先のオブジェクトも全て複製してコピーしたい場合を
ディープコピーと言うが、これを行うには自力で実装する必要がある。
こちらを参考にさせてもらって、汎用メソッド版と拡張メソッド版を以下のように用意した。
//--------------------------------------------------------------------------------
// 引数に渡したオブジェクトをディープコピーしたオブジェクトを生成して返す
// ジェネリックメソッド版
//--------------------------------------------------------------------------------
public static T DeepCopy<T>( T target )
{
T result;
BinaryFormatter b = new BinaryFormatter();
MemoryStream mem = new MemoryStream();
try {
b.Serialize(mem, target);
mem.Position = 0;
result = (T)b.Deserialize(mem);
}
finally {
mem.Close();
}
return result;
}
// 拡張メソッド版
public static object DeepCopy( this object target )
{
object result;
BinaryFormatter b = new BinaryFormatter();
MemoryStream mem = new MemoryStream();
try {
b.Serialize(mem, target);
mem.Position = 0;
result = b.Deserialize(mem);
}
finally {
mem.Close();
}
return result;
}
内容的には、BinaryFormatterによるシリアライズ、デシリアライズをMemoryStreamを介して行っているようなものか。
C#だとこの辺は少々めんどくさくなるが、致し方ない。
使用例はこんな感じ。
var hogeA = new HogeParam(); HogeParam hogeB; // ジェネリックメソッド版 hogeB = HogeUtil.DeepCopy( hogeA ); // 拡張メソッド版 hogeB = (HogeParam)hogeA.DeepCopy();
※
Unityでディープコピーを使用する際は、メンバ変数にMonoBehaviour継承のクラスといった
Unityで用意されている各コンポーネントクラスを持っている場合にはコピー出来ない(例外が発生する)ので注意。
(おそらくSystem.Serializable属性が指定されていない為と思われる。というかつけるわけがない・・・w)
参考:
http://smdn.jp/programming/netfx/cloning/
http://d.hatena.ne.jp/tekk/20091012/1255362429
http://d.hatena.ne.jp/tekk/20100131/1264913887