1. ホーム
  2. c#

[解決済み] ディープクローンオブジェクト

2022-03-16 13:03:27

質問

のようなことをしたいのですが。

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

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

私はこの機能をあまり必要としないので、必要なときは新しいオブジェクトを作成して、それぞれのプロパティを個別にコピーすることに頼っていますが、いつも、もっと良い、あるいはもっとエレガントな処理方法があるのではないかと感じています。

オブジェクトをクローンまたはディープコピーして、元のオブジェクトに変更を反映させずに、クローンしたオブジェクトを変更できるようにするにはどうすればよいですか?

どのように解決するのですか?

を実装する方法がありますが 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 stream = new MemoryStream();
        IFormatter formatter = new BinaryFormatter();
        formatter.Serialize(stream, source);
        stream.Seek(0, SeekOrigin.Begin);
        return (T)formatter.Deserialize(stream);
    }
}

オブジェクトをシリアライズし、それをデシリアライズして新しいオブジェクトにするというものです。その利点は、オブジェクトがあまりに複雑になったときに、すべてをクローンすることを気にする必要がないことです。

もし、新しい 拡張メソッド を以下のシグネチャに変更します。

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

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

EDIT (2015年1月10日)私は最近、これを行うために(Newtonsoft)Jsonを使用し始めた言及するために、私はこれを再訪しようと思いました、それ は、次のようになります。 の方が軽く、[Serializable]タグのオーバーヘッドを避けることができます。( NB コメントで@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);
}