C#でディープクローン(深いコピー)を行いたい場合、単に参照をコピーするだけではなく、オブジェクトの中のオブジェクトまで再帰的にコピーする必要があります。この記事では、ボードゲームのような複雑なオブジェクトをクローンする方法を解説し、実際にどうすればボードや駒をコピーして、元のオブジェクトが変更されないようにするかを紹介します。
1. シャローコピーとディープコピーの違い
まず、シャローコピー(浅いコピー)とディープコピー(深いコピー)の違いについて理解しておくことが重要です。シャローコピーは、オブジェクトの参照をコピーするだけであり、元のオブジェクトとコピー先のオブジェクトが同じメモリ空間を共有します。そのため、コピーしたオブジェクトを変更すると元のオブジェクトにも影響を与えます。
一方、ディープコピーはオブジェクトの中身を再帰的にコピーするため、コピー元とコピー先が全く独立した別のインスタンスとして扱われます。これにより、元のオブジェクトを変更してもコピー先のオブジェクトには影響がありません。
2. C#でディープクローンを実現する方法
C#でディープクローンを実現するためには、`ICloneable`インターフェースを実装して`Clone`メソッドをオーバーライドする方法が一般的です。ただし、`ICloneable`は浅いコピーを提供するだけなので、ディープコピーを行うためには手動でコピー処理を実装する必要があります。
例えば、ボードゲームのクラスである`Board`をコピーする際、以下のように配列やリストを再度コピーする必要があります。
public class Board : ICloneable
{
public int[] board = new int[8 * 8];
public Board()
{
for (int y = 0; y <= 7; y++)
{
for (int x = 0; x <= 7; x++)
{
board[(y << 3) + x] = Soldier.EMPTY;
}
}
}
public object Clone()
{
var clonedBoard = new Board();
clonedBoard.board = (int[])this.board.Clone();
return clonedBoard;
}
}
このコードでは、`board`配列を`Clone()`メソッドで再帰的にコピーすることで、ディープコピーを実現しています。`board.Clone()`は配列自体のコピーを行うので、元の配列の変更がコピー先に影響しません。
3. 参照型のディープコピーを手動で実装する
上記の例では、配列のコピーだけでなく、もし`Board`クラスが参照型(例えば、他のオブジェクトを含んでいる場合)であれば、参照型オブジェクトも再帰的にコピーする必要があります。例えば、`Board`が`Piece`オブジェクトのリストを保持している場合、リスト内の各オブジェクトも個別にコピーする必要があります。
以下のように、`Piece`クラスもクローン可能である必要があります。
public class Piece : ICloneable
{
public int type;
public object Clone()
{
return new Piece { type = this.type };
}
}
そして、`Board`クラスでは、`Piece`オブジェクトのリストをディープコピーするため、次のようにします。
public class Board : ICloneable
{
public List pieces = new List();
public object Clone()
{
var clonedBoard = new Board();
clonedBoard.pieces = this.pieces.Select(piece => (Piece)piece.Clone()).ToList();
return clonedBoard;
}
}
4. シリアライズを使ったディープコピー
もし手動でのクローン実装が煩雑である場合、C#ではシリアライズを使って簡単にディープコピーを行う方法もあります。オブジェクトを一度バイトストリームに変換し、再度復元することで、簡単にディープコピーを作成することができます。
以下のコード例では、`BinaryFormatter`を使ってディープコピーを行っています。
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
public static T DeepClone(T obj)
{
using (var ms = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(ms, obj);
ms.Seek(0, SeekOrigin.Begin);
return (T)formatter.Deserialize(ms);
}
}
この方法では、シリアライズ可能なオブジェクトを簡単にディープコピーできますが、パフォーマンスの面で若干のコストがかかる点に注意が必要です。
まとめ
C#でディープクローンを行う方法には、`Clone()`メソッドをオーバーライドして手動でコピーを実装する方法と、シリアライズを利用してディープコピーを作成する方法があります。どちらの方法も、オブジェクトの中の参照型オブジェクトを正しくコピーするために重要な手法です。ボードゲームなどの複雑なオブジェクトを扱う際は、これらの技術を活用して、コピー先での変更が元のオブジェクトに影響を与えないようにしましょう。


コメント