.Netのディープコピーとシャローコピーの2セント

Microsoft .Netは、オブジェクトのクローン作成のサポートを提供します。これは、オブジェクトの正確なコピー(クローンとも呼ばれます)を作成する機能です。クローニングには、浅いコピーと深いコピーの2つのタイプがあります。前者はSystem.ObjectクラスのMemberwiseCloneメソッドを呼び出すことで実装できますが、後者の実装は、デフォルトでフレームワークでサポートされていないため、少し注意が必要です。本質的に、浅いコピーは参照されたオブジェクトを除いて参照をコピーしますが、深いクローンはその参照とともにソースオブジェクトのコピーを作成します。

クローン作成に利用できるすべてのオプションは何ですか?

C#でクラスのインスタンスを複製するには、いくつかのオプションから選択できます。これらには次のものが含まれます。

  • System.Object.MemberwiseCloneメソッドを使用して浅いコピーを実行する
  • Activator.CreateInstanceメソッドを利用してReflectionを使用する
  • シリアル化の使用
  • IClonableインターフェースを実装することにより

.Netでクラスのオブジェクトまたはインスタンスを複製する場合、静的メンバーまたは静的フィールドを考慮する必要がないことに注意してください。その理由は、静的オブジェクトは共有メモリの場所に格納されており、アプリケーションドメインごとに1つのメモリの場所が割り当てられているためです。

浅いコピーと深いコピー

クラスEmployeeについて考えてみます。次に示すように、Employeeクラスのインスタンスを作成します。

Employee emp = new Employee();

Employee clone = emp;

上記のコードスニペットを参照してください。代入演算子 "="は、実際のオブジェクトではなく、参照をコピーします。System.Objectクラスで定義されたMemberwiseClone()メソッドは、まったく同じことを行います。これらは浅いコピーの例です。したがって、代入演算子を使用して別のオブジェクトにコピーしてオブジェクトを作成する場合、またはMemberwise.Clone()メソッドを使用する場合、実際にはオブジェクトの浅いコピーを実行しています。

浅いコピーでは、コピーされたオブジェクトのメンバーは元のオブジェクトと同じオブジェクトを参照しますが、深いコピーでは、元のインスタンスの各参照型メンバーの個別のインスタンスが、新しいインスタンスまたは複製されたインスタンスに作成されます。したがって、元のインスタンスに参照型がある場合、新しいインスタンスにも同じ参照型メンバーが含まれますが、この参照型はまったく新しいインスタンスを指します。

シャローコピーでは、新しいオブジェクトが作成され、ソースオブジェクトの非静的メンバーがターゲットオブジェクトまたは新しいオブジェクトにコピーされます。メンバーが値型フィールドの場合、フィールドのビットごとのコピーが実行されます。対照的に、コピーされるメンバーが参照型の場合、参照がコピーされます。したがって、元のオブジェクトとターゲットオブジェクト内の参照メンバーは、メモリ内の同じオブジェクトを参照します。

内部に個々の要素を含むコレクションがあり、コレクションインスタンスの浅いコピーを実行する場合。コレクションインスタンスの浅いコピーはコレクションの構造をコピーしますが、コレクション内の要素はコピーしないことに注意してください。したがって、コレクションインスタンスの浅いコピーを実行すると、コレクションの個々の要素を共有する2つのコレクションが作成されます。逆に、コレクションインスタンスのディープコピーを実行すると、元のコレクションの個々の要素が複製された2つのコレクションインスタンスが作成されます。

シリアル化を使用したディープコピーの実装

ディープコピーはさまざまな方法で実装できます。オブジェクトのディープコピーを実装するための最も好ましい方法の1つは、シリアル化を使用することです。リフレクションを利用して、クラスのインスタンスのディープコピーを実行することもできます。次のコードスニペットは、バイナリシリアル化を実装して、C#を使用してインスタンスのディープコピーを実行するメソッドを作成する方法を示しています。

public static T DeepCopy(T obj)

       {

           if (!typeof(T).IsSerializable)

           {

               throw new Exception("The source object must be serializable");

           }

           if (Object.ReferenceEquals(obj, null))

           {

               throw new Exception("The source object must not be null");

           }

           T result = default(T);

           using (var memoryStream = new MemoryStream())

           {

                var formatter = new BinaryFormatter();

               formatter.Serialize(memoryStream, obj);

               memoryStream.Seek(0, SeekOrigin.Begin);

               result = (T)formatter.Deserialize(memoryStream);

               memoryStream.Close();

           }

           return result;

       }

Employeeというエンティティクラスがあることを考えると、以下のコードスニペットに示すように、Employeeクラスのインスタンスのディープコピーを実行できます。

static void Main(string[] args)

       {

           Employee emp = new Employee();

           emp.EmployeeId = 1;

           emp.FirstName = "Joydip";

           emp.LastName = "Kanjilal";

           Employee clone = DeepCopy(emp);

           if(Object.ReferenceEquals(emp, clone))

           {

               Console.WriteLine("References are the same.");

           }

           else

           {

               Console.WriteLine("References are different.");

           }

       }

上記のプログラムを実行すると、インスタンス「emp」のディープコピーが実行され、「参照が異なります」というメッセージが表示されます。が表示されます。