2562 votes

Profonde clonage des objets

Je veux faire quelque chose comme:

myObject myObj = GetmyObj(); //Create and fill a new object
myObject newObj = myObj.Clone();

Et puis faire des changements pour le nouvel objet qui ne sont pas reflétés dans l'objet d'origine.

Je n'ai pas souvent besoin de cette fonctionnalité, donc quand il a été nécessaire, j'ai eu recours à la création d'un nouvel objet, puis de le copier chaque propriété individuellement, mais il me laisse toujours avec le sentiment qu'il existe une meilleure ou la plus élégante façon de gérer la situation.

Comment puis-je cloner ou copie en profondeur d'un objet, de sorte que l'objet cloné peut être modifiée sans changements, qui se reflète dans l'objet d'origine?

1892voto

johnc Points 12140

Alors que la norme est de mettre en œuvre l' ICloneable (interface décrite ici, donc je ne vais pas régurgiter), voilà une belle profondeur cloner un objet copieur j'ai trouvé sur Le Projet de Code il y a un moment et l'a incorporé dans nos affaires.

Comme mentionné ailleurs, il n'a besoin de vos objets sérialisables.

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.
    /// </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 Clone<T>(T source)
    {
        if (!typeof(T).IsSerializable)
        {
            throw new ArgumentException("The type must be serializable.", "source");
        }

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

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

L'idée est qu'il sérialise votre objet, puis désérialise dans une nouvelle objet. L'avantage est que vous n'avez pas à vous soucier à propos du clonage tout quand un objet devient trop complexe.

Et avec l'utilisation de méthodes d'extension (également à partir de l'origine référencé source):

Dans le cas où vous préférez utiliser les nouvelles méthodes d'extension de C# 3.0, changer la méthode pour avoir la signature suivante:

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

Maintenant, l'appel de la méthode devient tout simplement objectBeingCloned.Clone();.

404voto

craastad Points 697

Je voulais un cloneur pour des objets très simples de la plupart des primitives et des listes. Si votre objet est en dehors de la zone de JSON serializable cette méthode fera l'affaire. Cela ne nécessite aucune modification ou la mise en œuvre des interfaces sur la cloné classe, juste un sérialiseur JSON comme JSON.NET.

public static T Clone<T>(T source)
{
    var serialized = JsonConvert.SerializeObject(source);
    return JsonConvert.DeserializeObject<T>(serialized);
}

196voto

Kyralessa Points 76456

La raison de ne pas utiliser ICloneable est pas parce qu'il ne dispose pas d'une interface générique. La raison de ne pas l'utiliser parce que c'est vague. Il ne fait pas clair de savoir si vous obtenez un plat peu profond ou d'une copie en profondeur; c'est à l'oeuvre.

Oui, MemberwiseClone rend une copie superficielle, mais à l'opposé de MemberwiseClone n'est pas Clone; il serait, peut-être, DeepClone, ce qui n'existe pas. Lorsque vous utilisez un objet par le biais de son interface ICloneable, vous ne savez pas quel type de clonage de l'objet sous-jacent effectue. (Et des commentaires XML ne le fera pas clair, parce que vous obtiendrez l'interface des commentaires plutôt que sur l'objet de la méthode Clone.)

Ce que j'ai l'habitude de faire est de simplement faire une Copy méthode qui fait exactement ce que je veux.

117voto

Ian P Points 7930

Voici une bonne ressource que j'ai utilisé dans le passé: Objet C# Clonage

95voto

Nick Points 7173

Je préfère un constructeur de copie pour un clone. L'intention est claire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X