ディープクローンオブジェクト 質問する

ディープクローンオブジェクト 質問する

次のようなことをしたいです:

MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = myObj.Clone();

そして、元のオブジェクトに反映されていない変更を新しいオブジェクトに加えます。

この機能が必要になることはあまりないので、必要な場合は、新しいオブジェクトを作成して各プロパティを個別にコピーする方法に頼ってきましたが、状況に対処するにはもっと良い、またはよりエレガントな方法があるのではないかという思いが常に残ります。

元のオブジェクトに変更が反映されることなく、複製されたオブジェクトを変更できるように、オブジェクトを複製またはディープコピーするにはどうすればよいですか?

ベストアンサー1

一つのアプローチは、ICloneableインターフェース(説明ここ(なので、私は繰り返しません)ここで私が見つけた素晴らしいディープクローンオブジェクトコピーツールを紹介します。コードプロジェクトしばらく前にこれをコードに組み込みました。他のところで述べたように、オブジェクトがシリアル化可能であることが必要です。

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

/// <summary>
/// Reference Article http://www.codeproject.com/KB/tips/SerializedObjectCloner.aspx
/// Provides a method for performing a deep copy of an object.
/// Binary Serialization is used to perform the copy.
/// </summary>
public static class ObjectCopier
{
    /// <summary>
    /// Perform a deep copy of the object via serialization.
    /// </summary>
    /// <typeparam name="T">The type of object being copied.</typeparam>
    /// <param name="source">The object instance to copy.</param>
    /// <returns>A deep copy of the object.</returns>
    public static T Clone<T>(T source)
    {
        if (!typeof(T).IsSerializable)
        {
            throw new ArgumentException("The type must be serializable.", nameof(source));
        }

        // Don't serialize a null object, simply return the default for that object
        if (ReferenceEquals(source, null)) return default;

        using var stream = new MemoryStream();
        IFormatter formatter = new BinaryFormatter();
        formatter.Serialize(stream, source);
        stream.Seek(0, SeekOrigin.Begin);
        return (T)formatter.Deserialize(stream);
    }
}

オブジェクトをシリアル化し、それを新しいオブジェクトにデシリアル化するという考え方です。利点は、オブジェクトが複雑になりすぎたときにすべてを複製する必要がないことです。

新しいものを使いたい場合は拡張メソッドC# 3.0 では、メソッドを次のシグネチャに変更します。

public static T Clone<T>(this T source)
{
   // ...
}

これで、メソッド呼び出しは単純に になりますobjectBeingCloned.Clone();

編集(2015年1月10日)これをもう一度見直してみようと思ったのですが、最近これをするために(Newtonsoft)Jsonを使い始めたのですが、すべきである軽量化され、[Serializable] タグのオーバーヘッドを回避します。(注: @atconway はコメントで、プライベート メンバーは JSON メソッドを使用して複製されないことを指摘しています)

/// <summary>
/// Perform a deep Copy of the object, using Json as a serialization method. NOTE: Private members are not cloned using this method.
/// </summary>
/// <typeparam name="T">The type of object being copied.</typeparam>
/// <param name="source">The object instance to copy.</param>
/// <returns>The copied object.</returns>
public static T CloneJson<T>(this T source)
{            
    // Don't serialize a null object, simply return the default for that object
    if (ReferenceEquals(source, null)) return default;

    // initialize inner objects individually
    // for example in default constructor some list property initialized with some values,
    // but in 'source' these items are cleaned -
    // without ObjectCreationHandling.Replace default constructor values will be added to result
    var deserializeSettings = new JsonSerializerSettings {ObjectCreationHandling = ObjectCreationHandling.Replace};

    return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source), deserializeSettings);
}

おすすめ記事