c# - objects - vb net clone object




Oggetti di clonazione profonda (20)

Voglio fare qualcosa come:

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

E quindi apportare modifiche al nuovo oggetto che non si riflettono nell'oggetto originale.

Non ho spesso bisogno di questa funzionalità, quindi quando è stato necessario, ho fatto ricorso alla creazione di un nuovo oggetto e quindi a copiare ciascuna proprietà singolarmente, ma mi lascia sempre la sensazione che esista un modo migliore o più elegante di gestire la situazione.

Come posso clonare o copiare in profondità un oggetto in modo che l'oggetto clonato possa essere modificato senza che nessuna modifica venga riflessa nell'oggetto originale?


Generatore di codici

Abbiamo visto molte idee dalla serializzazione sull'implementazione manuale alla riflessione e voglio proporre un approccio totalmente diverso utilizzando il generatore di codice CGbR . Il metodo clone di generazione è efficiente in termini di memoria e CPU e quindi 300 volte più veloce del DataContractSerializer standard.

Tutto ciò di cui hai bisogno è una definizione di classe parziale con ICloneablee il generatore fa il resto:

public partial class Root : ICloneable
{
    public Root(int number)
    {
        _number = number;
    }
    private int _number;

    public Partial[] Partials { get; set; }

    public IList<ulong> Numbers { get; set; }

    public object Clone()
    {
        return Clone(true);
    }

    private Root()
    {
    }
} 

public partial class Root
{
    public Root Clone(bool deep)
    {
        var copy = new Root();
        // All value types can be simply copied
        copy._number = _number; 
        if (deep)
        {
            // In a deep clone the references are cloned 
            var tempPartials = new Partial[Partials.Length];
            for (var i = 0; i < Partials.Length; i++)
            {
                var value = Partials[i];
                value = value.Clone(true);
                tempPartials[i] = value;
            }
            copy.Partials = tempPartials;
            var tempNumbers = new List<ulong>(Numbers.Count);
            for (var i = 0; i < Numbers.Count; i++)
            {
                var value = Numbers[i];
                tempNumbers.Add(value);
            }
            copy.Numbers = tempNumbers;
        }
        else
        {
            // In a shallow clone only references are copied
            copy.Partials = Partials; 
            copy.Numbers = Numbers; 
        }
        return copy;
    }
}

Nota: l' ultima versione ha più assegni nulli, ma li ho lasciati fuori per una migliore comprensione.


D. Perché dovrei scegliere questa risposta?

  • Scegli questa risposta se desideri la massima velocità di .NET.
  • Ignora questa risposta se vuoi un metodo di clonazione davvero facile.

In altre parole, vai con un'altra risposta a meno che tu non abbia un collo di bottiglia delle prestazioni che deve essere corretto, e puoi provarlo con un profiler .

10 volte più veloce rispetto ad altri metodi

Il seguente metodo per eseguire un clone profondo è:

  • 10 volte più veloce di qualsiasi cosa che implichi serializzazione / deserializzazione;
  • Abbastanza maledettamente vicino alla massima velocità teorica di cui .NET è capace.

E il metodo ...

Per la massima velocità, puoi usare Nested MemberwiseClone per fare una copia profonda . Ha quasi la stessa velocità con cui copia una struct value, ed è molto più veloce della (a) reflection o (b) serializzazione (come descritto in altre risposte in questa pagina).

Si noti che se si utilizza Nested MemberwiseClone per una copia profonda , è necessario implementare manualmente un ShallowCopy per ogni livello nidificato nella classe e un DeepCopy che chiama tutti i suddetti metodi di ShallowCopy per creare un clone completo. Questo è semplice: solo poche righe in totale, vedi il codice demo qui sotto.

Ecco l'output del codice che mostra la differenza relativa delle prestazioni per 100.000 cloni:

  • 1,08 secondi per Nested MemberwiseClone su strutture annidate
  • 4,77 secondi per Nested MemberwiseClone su classi annidate
  • 39,93 secondi per la serializzazione / deserializzazione

Utilizzando Nested MemberwiseClone su una classe quasi veloce come copiare una struct, e copiare una struct è dannatamente vicino alla velocità massima teorica di cui .NET è capace.

Demo 1 of shallow and deep copy, using classes and MemberwiseClone:
  Create Bob
    Bob.Age=30, Bob.Purchase.Description=Lamborghini
  Clone Bob >> BobsSon
  Adjust BobsSon details
    BobsSon.Age=2, BobsSon.Purchase.Description=Toy car
  Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:
    Bob.Age=30, Bob.Purchase.Description=Lamborghini
  Elapsed time: 00:00:04.7795670,30000000

Demo 2 of shallow and deep copy, using structs and value copying:
  Create Bob
    Bob.Age=30, Bob.Purchase.Description=Lamborghini
  Clone Bob >> BobsSon
  Adjust BobsSon details:
    BobsSon.Age=2, BobsSon.Purchase.Description=Toy car
  Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:
    Bob.Age=30, Bob.Purchase.Description=Lamborghini
  Elapsed time: 00:00:01.0875454,30000000

Demo 3 of deep copy, using class and serialize/deserialize:
  Elapsed time: 00:00:39.9339425,30000000

Per capire come fare una copia approfondita usando MemberwiseCopy, ecco il progetto dimostrativo che è stato usato per generare le volte sopra:

// Nested MemberwiseClone example. 
// Added to demo how to deep copy a reference class.
[Serializable] // Not required if using MemberwiseClone, only used for speed comparison using serialization.
public class Person
{
    public Person(int age, string description)
    {
        this.Age = age;
        this.Purchase.Description = description;
    }
    [Serializable] // Not required if using MemberwiseClone
    public class PurchaseType
    {
        public string Description;
        public PurchaseType ShallowCopy()
        {
            return (PurchaseType)this.MemberwiseClone();
        }
    }
    public PurchaseType Purchase = new PurchaseType();
    public int Age;
    // Add this if using nested MemberwiseClone.
    // This is a class, which is a reference type, so cloning is more difficult.
    public Person ShallowCopy()
    {
        return (Person)this.MemberwiseClone();
    }
    // Add this if using nested MemberwiseClone.
    // This is a class, which is a reference type, so cloning is more difficult.
    public Person DeepCopy()
    {
            // Clone the root ...
        Person other = (Person) this.MemberwiseClone();
            // ... then clone the nested class.
        other.Purchase = this.Purchase.ShallowCopy();
        return other;
    }
}
// Added to demo how to copy a value struct (this is easy - a deep copy happens by default)
public struct PersonStruct
{
    public PersonStruct(int age, string description)
    {
        this.Age = age;
        this.Purchase.Description = description;
    }
    public struct PurchaseType
    {
        public string Description;
    }
    public PurchaseType Purchase;
    public int Age;
    // This is a struct, which is a value type, so everything is a clone by default.
    public PersonStruct ShallowCopy()
    {
        return (PersonStruct)this;
    }
    // This is a struct, which is a value type, so everything is a clone by default.
    public PersonStruct DeepCopy()
    {
        return (PersonStruct)this;
    }
}
// Added only for a speed comparison.
public class MyDeepCopy
{
    public static T DeepCopy<T>(T obj)
    {
        object result = null;
        using (var ms = new MemoryStream())
        {
            var formatter = new BinaryFormatter();
            formatter.Serialize(ms, obj);
            ms.Position = 0;
            result = (T)formatter.Deserialize(ms);
            ms.Close();
        }
        return (T)result;
    }
}

Quindi, chiama la demo dal principale:

void MyMain(string[] args)
{
    {
        Console.Write("Demo 1 of shallow and deep copy, using classes and MemberwiseCopy:\n");
        var Bob = new Person(30, "Lamborghini");
        Console.Write("  Create Bob\n");
        Console.Write("    Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);
        Console.Write("  Clone Bob >> BobsSon\n");
        var BobsSon = Bob.DeepCopy();
        Console.Write("  Adjust BobsSon details\n");
        BobsSon.Age = 2;
        BobsSon.Purchase.Description = "Toy car";
        Console.Write("    BobsSon.Age={0}, BobsSon.Purchase.Description={1}\n", BobsSon.Age, BobsSon.Purchase.Description);
        Console.Write("  Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:\n");
        Console.Write("    Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);
        Debug.Assert(Bob.Age == 30);
        Debug.Assert(Bob.Purchase.Description == "Lamborghini");
        var sw = new Stopwatch();
        sw.Start();
        int total = 0;
        for (int i = 0; i < 100000; i++)
        {
            var n = Bob.DeepCopy();
            total += n.Age;
        }
        Console.Write("  Elapsed time: {0},{1}\n\n", sw.Elapsed, total);
    }
    {               
        Console.Write("Demo 2 of shallow and deep copy, using structs:\n");
        var Bob = new PersonStruct(30, "Lamborghini");
        Console.Write("  Create Bob\n");
        Console.Write("    Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);
        Console.Write("  Clone Bob >> BobsSon\n");
        var BobsSon = Bob.DeepCopy();
        Console.Write("  Adjust BobsSon details:\n");
        BobsSon.Age = 2;
        BobsSon.Purchase.Description = "Toy car";
        Console.Write("    BobsSon.Age={0}, BobsSon.Purchase.Description={1}\n", BobsSon.Age, BobsSon.Purchase.Description);
        Console.Write("  Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:\n");
        Console.Write("    Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);                
        Debug.Assert(Bob.Age == 30);
        Debug.Assert(Bob.Purchase.Description == "Lamborghini");
        var sw = new Stopwatch();
        sw.Start();
        int total = 0;
        for (int i = 0; i < 100000; i++)
        {
            var n = Bob.DeepCopy();
            total += n.Age;
        }
        Console.Write("  Elapsed time: {0},{1}\n\n", sw.Elapsed, total);
    }
    {
        Console.Write("Demo 3 of deep copy, using class and serialize/deserialize:\n");
        int total = 0;
        var sw = new Stopwatch();
        sw.Start();
        var Bob = new Person(30, "Lamborghini");
        for (int i = 0; i < 100000; i++)
        {
            var BobsSon = MyDeepCopy.DeepCopy<Person>(Bob);
            total += BobsSon.Age;
        }
        Console.Write("  Elapsed time: {0},{1}\n", sw.Elapsed, total);
    }
    Console.ReadKey();
}

Di nuovo, si noti che se si utilizza Nested MemberwiseClone per una copia profonda , è necessario implementare manualmente ShallowCopy per ogni livello nidificato nella classe e un DeepCopy che chiama tutti i suddetti metodi di ShallowCopy per creare un clone completo. Questo è semplice: solo poche righe in totale, vedi il codice demo qui sopra.

Tipi di valore e tipi di referenze

Nota che quando si tratta di clonare un oggetto, c'è una grande differenza tra una " struct " e una " class ":

  • Se hai una " struct ", è un tipo di valore in modo che tu possa semplicemente copiarlo, e il contenuto sarà clonato (ma farà solo un clone superficiale a meno che non usi le tecniche in questo post).
  • Se hai una " classe ", è un tipo di riferimento , quindi se lo copi, tutto ciò che stai facendo è copiare il puntatore su di esso. Per creare un vero clone, devi essere più creativo e utilizzare le differenze tra tipi di valori e tipi di riferimenti che creano un'altra copia dell'oggetto originale in memoria.

Visualizza le differenze tra tipi di valore e tipi di riferimenti .

Checksum per aiutare nel debugging

  • La clonazione non corretta degli oggetti può portare a bug molto difficili da decifrare. Nel codice di produzione, tendo ad implementare un checksum per verificare che l'oggetto sia stato clonato correttamente e non sia stato danneggiato da un altro riferimento ad esso. Questo checksum può essere disattivato in modalità di rilascio.
  • Trovo abbastanza utile questo metodo: spesso, vuoi solo clonare parti dell'oggetto, non l'intera cosa.

Davvero utile per disaccoppiare molti thread da molti altri thread

Un caso d'uso eccellente per questo codice è l'alimentazione di cloni di una classe nidificata o di una struttura in una coda, per implementare il modello produttore / consumatore.

  • Possiamo avere uno (o più) thread che modificano una classe che possiedono, quindi spingere una copia completa di questa classe in a ConcurrentQueue.
  • Abbiamo quindi uno (o più) thread che tirano fuori copie di queste classi e le gestiscono.

Questo funziona molto bene nella pratica e ci consente di disaccoppiare molti thread (i produttori) da uno o più thread (i consumatori).

E questo metodo è anche incredibilmente veloce: se usiamo le strutture annidate, è 35 volte più veloce della serializzazione / deserializzazione delle classi annidate e ci consente di sfruttare tutti i thread disponibili sulla macchina.

Aggiornare

Apparentemente, ExpressMapper è veloce, se non più veloce, della codifica manuale come sopra. Potrei dover vedere come si confronta con un profiler.


Beh, stavo avendo problemi con l'uso di ICloneable in Silverlight, ma mi piaceva l'idea della seralizzazione, posso seralizzare l'XML, così ho fatto questo:

static public class SerializeHelper
{
    //Michael White, Holly Springs Consulting, 2009
    //[email protected]
    public static T DeserializeXML<T>(string xmlData) where T:new()
    {
        if (string.IsNullOrEmpty(xmlData))
            return default(T);

        TextReader tr = new StringReader(xmlData);
        T DocItms = new T();
        XmlSerializer xms = new XmlSerializer(DocItms.GetType());
        DocItms = (T)xms.Deserialize(tr);

        return DocItms == null ? default(T) : DocItms;
    }

    public static string SeralizeObjectToXML<T>(T xmlObject)
    {
        StringBuilder sbTR = new StringBuilder();
        XmlSerializer xmsTR = new XmlSerializer(xmlObject.GetType());
        XmlWriterSettings xwsTR = new XmlWriterSettings();

        XmlWriter xmwTR = XmlWriter.Create(sbTR, xwsTR);
        xmsTR.Serialize(xmwTR,xmlObject);

        return sbTR.ToString();
    }

    public static T CloneObject<T>(T objClone) where T:new()
    {
        string GetString = SerializeHelper.SeralizeObjectToXML<T>(objClone);
        return SerializeHelper.DeserializeXML<T>(GetString);
    }
}

Dopo molte letture su molte delle opzioni collegate qui, e possibili soluzioni per questo problema, credo che tutte le opzioni siano sintetizzate abbastanza bene al link di Ian P (tutte le altre opzioni sono variazioni di quelle) e la migliore soluzione è fornita da agiledeveloper.com/articles/cloning072002.htm sui commenti delle domande.

Quindi copro solo le parti rilevanti di questi 2 riferimenti qui. In questo modo possiamo avere:

La cosa migliore da fare per la clonazione di oggetti in c sharp!

Innanzitutto, quelle sono tutte le nostre opzioni:

L' articolo Fast Deep Copy di Expression Trees ha anche un confronto delle prestazioni della clonazione tramite Serialization, Reflection ed Expression Trees.

Perché scelgo ICloneable (cioè manualmente)

agiledeveloper.com/articles/cloning072002.htm .

Tutto il suo articolo circonda un esempio che cerca di essere applicabile per la maggior parte dei casi, utilizzando 3 oggetti: Person , Brain e City . Vogliamo clonare una persona, che avrà il suo cervello ma la stessa città. È possibile visualizzare tutti i problemi uno qualsiasi degli altri metodi sopra riportati può portare o leggere l'articolo.

Questa è la mia versione leggermente modificata della sua conclusione:

Copiare un oggetto specificando New seguito dal nome della classe porta spesso a un codice che non è estensibile. L'uso del clone, l'applicazione del modello prototipo, è un modo migliore per raggiungere questo obiettivo. Tuttavia, l'uso del clone come è fornito in C # (e Java) può essere piuttosto problematico. È meglio fornire un costruttore di copia protetto (non pubblico) e invocarlo dal metodo clone. Questo ci dà la possibilità di delegare il compito di creare un oggetto a un'istanza di una classe stessa, fornendo così estensibilità e anche, creando in sicurezza gli oggetti usando il costruttore di copia protetta.

Speriamo che questa implementazione possa chiarire le cose:

public class Person : ICloneable
{
    private final Brain brain; // brain is final since I do not want 
                // any transplant on it once created!
    private int age;
    public Person(Brain aBrain, int theAge)
    {
        brain = aBrain; 
        age = theAge;
    }
    protected Person(Person another)
    {
        Brain refBrain = null;
        try
        {
            refBrain = (Brain) another.brain.clone();
            // You can set the brain in the constructor
        }
        catch(CloneNotSupportedException e) {}
        brain = refBrain;
        age = another.age;
    }
    public String toString()
    {
        return "This is person with " + brain;
        // Not meant to sound rude as it reads!
    }
    public Object clone()
    {
        return new Person(this);
    }
    …
}

Ora considera che una classe deriva da Person.

public class SkilledPerson extends Person
{
    private String theSkills;
    public SkilledPerson(Brain aBrain, int theAge, String skills)
    {
        super(aBrain, theAge);
        theSkills = skills;
    }
    protected SkilledPerson(SkilledPerson another)
    {
        super(another);
        theSkills = another.theSkills;
    }

    public Object clone()
    {
        return new SkilledPerson(this);
    }
    public String toString()
    {
        return "SkilledPerson: " + super.toString();
    }
}

Puoi provare a eseguire il seguente codice:

public class User
{
    public static void play(Person p)
    {
        Person another = (Person) p.clone();
        System.out.println(p);
        System.out.println(another);
    }
    public static void main(String[] args)
    {
        Person sam = new Person(new Brain(), 1);
        play(sam);
        SkilledPerson bob = new SkilledPerson(new SmarterBrain(), 1, "Writer");
        play(bob);
    }
}

L'output prodotto sarà:

This is person with [email protected]
This is person with [email protected]
SkilledPerson: This is person with [email protected]
SkilledPerson: This is person with [email protected]

Osserva che, se teniamo un conteggio del numero di oggetti, il clone come implementato qui manterrà un conteggio corretto del numero di oggetti.


La cosa migliore è implementare un metodo di estensione come

public static T DeepClone<T>(this T originalObject)
{ /* the cloning code */ }

e quindi utilizzarlo ovunque nella soluzione

var copy = anyObject.DeepClone();

Possiamo avere le seguenti tre implementazioni:

  1. Per serializzazione (il codice più breve)
  2. Con Reflection - 5 volte più veloce
  3. Da Expression Trees - 20 volte più veloce

Tutti i metodi collegati funzionano bene e sono stati profondamente testati.


La ragione per non usare ICloneable non è perché non ha un'interfaccia generica. La ragione per non usarlo è perché è vaga . Non è chiaro se stai ricevendo una copia superficiale o profonda; questo dipende dall'implementatore.

Sì, MemberwiseClone fa una copia superficiale, ma il contrario di MemberwiseClone non è Clone ; sarebbe, forse, DeepClone , che non esiste. Quando si utilizza un oggetto attraverso la sua interfaccia ICloneable, non si può sapere quale tipo di clonazione viene eseguita dall'oggetto sottostante. (E i commenti XML non lo renderanno chiaro, perché otterrete i commenti dell'interfaccia piuttosto che quelli sul metodo Clone dell'oggetto.)

Quello che faccio di solito è semplicemente fare un metodo di Copy che fa esattamente quello che voglio.


Mentre la pratica standard è quella di implementare l'interfaccia ICloneable ( here descritta, quindi non voglio rigurgitare), ecco una copiatrice di oggetti clone profonda che ho trovato su The Code Project qualche tempo fa e l'ho incorporata nelle nostre cose.

Come accennato altrove, richiede che i tuoi oggetti siano serializzabili.

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'idea è che serializza il tuo oggetto e poi lo deserializza in un nuovo oggetto. Il vantaggio è che non devi preoccuparti di clonare tutto quando un oggetto diventa troppo complesso.

E con l'uso di metodi di estensione (anche dalla fonte di riferimento originale):

Se si preferisce utilizzare i nuovi metodi di estensione di C # 3.0, cambiare il metodo per ottenere la seguente firma:

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

Ora la chiamata al metodo diventa semplicemente objectBeingCloned.Clone(); .

EDIT (10 gennaio 2015) Ho pensato di rivisitare questo, per dire che recentemente ho iniziato a usare (Newtonsoft) Json per farlo, dovrebbe essere più leggero ed evitare il sovraccarico dei tag [Serializable]. ( NB @atconway ha sottolineato nei commenti che i membri privati ​​non vengono clonati usando il metodo JSON)

/// <summary>
/// Perform a deep Copy of the object, using Json as a serialisation 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 (Object.ReferenceEquals(source, null))
    {
        return default(T);
    }

    // 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);
}

Preferisco un costruttore di copia a un clone. L'intento è più chiaro.


Se vuoi la vera clonazione a tipi sconosciuti puoi dare un'occhiata a fastclone .

Questa è la clonazione basata su espressioni che funziona circa 10 volte più velocemente della serializzazione binaria e mantiene l'integrità completa del grafico degli oggetti.

Ciò significa: se ti riferisci più volte allo stesso oggetto nella tua gerarchia, il clone avrà anche una singola istanza referenziata.

Non sono necessarie interfacce, attributi o altre modifiche agli oggetti da clonare.


Segui questi passi:

  • Definisci un oggetto ISelf<T>con una Selfproprietà di sola lettura che restituisce Te ICloneable<out T>, che deriva da ISelf<T>e include un metodo T Clone().
  • Quindi definire un CloneBasetipo che implementa un protected virtual generic VirtualClonecasting MemberwiseCloneper il tipo passato.
  • Ogni tipo derivato dovrebbe implementare VirtualClonechiamando il metodo clone di base e quindi facendo tutto il necessario per clonare correttamente quegli aspetti del tipo derivato che il metodo padre VirtualClone non ha ancora gestito.

Per la massima versatilità di ereditarietà, le classi che espongono la funzionalità di clonazione pubblica dovrebbero essere sealed, ma derivano da una classe di base che è altrimenti identica ad eccezione della mancanza di clonazione. Piuttosto che passare le variabili del tipo clonable esplicito, prendi un parametro di tipo ICloneable<theNonCloneableType>. Ciò consentirà una routine che prevede che un derivato clonabile Foofunzioni con una derivata clonabile di DerivedFoo, ma consenta anche la creazione di derivati ​​non clonabili di Foo.


Volevo un cloner per oggetti molto semplici, per lo più primitivi e liste. Se il tuo oggetto è fuori dalla scatola serializzabile in JSON, allora questo metodo farà il trucco. Ciò non richiede alcuna modifica o implementazione di interfacce sulla classe clonata, ma solo un serializzatore JSON come JSON.NET.

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

Inoltre, puoi usare questo metodo di estensione

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

Ecco un'implementazione di copia profonda:

public static object CloneObject(object opSource)
{
    //grab the type and create a new instance of that type
    Type opSourceType = opSource.GetType();
    object opTarget = CreateInstanceOfType(opSourceType);

    //grab the properties
    PropertyInfo[] opPropertyInfo = opSourceType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

    //iterate over the properties and if it has a 'set' method assign it from the source TO the target
    foreach (PropertyInfo item in opPropertyInfo)
    {
        if (item.CanWrite)
        {
            //value types can simply be 'set'
            if (item.PropertyType.IsValueType || item.PropertyType.IsEnum || item.PropertyType.Equals(typeof(System.String)))
            {
                item.SetValue(opTarget, item.GetValue(opSource, null), null);
            }
            //object/complex types need to recursively call this method until the end of the tree is reached
            else
            {
                object opPropertyValue = item.GetValue(opSource, null);
                if (opPropertyValue == null)
                {
                    item.SetValue(opTarget, null, null);
                }
                else
                {
                    item.SetValue(opTarget, CloneObject(opPropertyValue), null);
                }
            }
        }
    }
    //return the new item
    return opTarget;
}

Mi piacciono i Copyconstructors in questo modo:

    public AnyObject(AnyObject anyObject)
    {
        foreach (var property in typeof(AnyObject).GetProperties())
        {
            property.SetValue(this, property.GetValue(anyObject));
        }
        foreach (var field in typeof(AnyObject).GetFields())
        {
            field.SetValue(this, field.GetValue(anyObject));
        }
    }

Se hai più cose da copiare aggiungili


Ok, ci sono alcuni esempi ovvi di riflessione in questo post, ma la riflessione è solitamente lenta, fino a quando non inizi a memorizzarla correttamente.

se lo memorizzi correttamente, allora clonerà in profondità 1000000 oggetti per 4,6 secondi (misurati da Watcher).

static readonly Dictionary<Type, PropertyInfo[]> ProperyList = new Dictionary<Type, PropertyInfo[]>();

di quanto si prendono le proprietà memorizzate nella cache o aggiungere nuovi al dizionario e usarli semplicemente

foreach (var prop in propList)
{
        var value = prop.GetValue(source, null);   
        prop.SetValue(copyInstance, value, null);
}

controllo completo del codice nel mio post in un'altra risposta

https://.com/a/34365709/4711853


Per clonare il tuo oggetto di classe puoi usare il metodo Object.MemberwiseClone,

aggiungi questa funzione alla tua classe:

public class yourClass
{
    // ...
    // ...

    public yourClass DeepCopy()
    {
        yourClass othercopy = (yourClass)this.MemberwiseClone();
        return othercopy;
    }
}

quindi per eseguire una copia indipendente, basta chiamare il metodo DeepCopy:

yourClass newLine = oldLine.DeepCopy();

spero che questo ti aiuti.


Questo metodo ha risolto il problema per me:

private static MyObj DeepCopy(MyObj source)
        {

            var DeserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };

            return JsonConvert.DeserializeObject<MyObj >(JsonConvert.SerializeObject(source), DeserializeSettings);

        }

Usalo in questo modo: MyObj a = DeepCopy(b);


Ho creato una versione della risposta accettata che funziona con '[Serializable]' e '[DataContract]'. È passato un po 'di tempo da quando l'ho scritto, ma se ricordo bene [DataContract] avevo bisogno di un serializzatore diverso.

Richiede System, System.IO, System.Runtime.Serialization, System.Runtime.Serialization.Formatters.Binary, System.Xml ;

public static class ObjectCopier
{

    /// <summary>
    /// Perform a deep Copy of an object that is marked with '[Serializable]' or '[DataContract]'
    /// </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 == true)
        {
            return CloneUsingSerializable<T>(source);
        }

        if (IsDataContract(typeof(T)) == true)
        {
            return CloneUsingDataContracts<T>(source);
        }

        throw new ArgumentException("The type must be Serializable or use DataContracts.", "source");
    }


    /// <summary>
    /// Perform a deep Copy of an object that is marked with '[Serializable]'
    /// </summary>
    /// <remarks>
    /// Found on http://.com/questions/78536/cloning-objects-in-c-sharp
    /// Uses code found on CodeProject, which allows free use in third party apps
    /// - http://www.codeproject.com/KB/tips/SerializedObjectCloner.aspx
    /// </remarks>
    /// <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 CloneUsingSerializable<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);
        }
    }


    /// <summary>
    /// Perform a deep Copy of an object that is marked with '[DataContract]'
    /// </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 CloneUsingDataContracts<T>(T source)
    {
        if (IsDataContract(typeof(T)) == false)
        {
            throw new ArgumentException("The type must be a data contract.", "source");
        }

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

        DataContractSerializer dcs = new DataContractSerializer(typeof(T));
        using(Stream stream = new MemoryStream())
        {
            using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(stream))
            {
                dcs.WriteObject(writer, source);
                writer.Flush();
                stream.Seek(0, SeekOrigin.Begin);
                using (XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(stream, XmlDictionaryReaderQuotas.Max))
                {
                    return (T)dcs.ReadObject(reader);
                }
            }
        }
    }


    /// <summary>
    /// Helper function to check if a class is a [DataContract]
    /// </summary>
    /// <param name="type">The type of the object to check.</param>
    /// <returns>Boolean flag indicating if the class is a DataContract (true) or not (false) </returns>
    public static bool IsDataContract(Type type)
    {
        object[] attributes = type.GetCustomAttributes(typeof(DataContractAttribute), false);
        return attributes.Length == 1;
    }

} 

In generale, si implementa l'interfaccia ICloneable e si implementa Clone autonomamente. Gli oggetti C # hanno un metodo MemberwiseClone incorporato che esegue una copia superficiale che può aiutarti per tutti i primitivi.

Per una copia profonda, non c'è modo di sapere come farlo automaticamente.


Mantieni le cose semplici e usa AutoMapper come già detto, è una semplice piccola libreria per mappare un oggetto con un altro ... Per copiare un oggetto con un altro dello stesso tipo, tutto ciò che ti serve sono tre righe di codice:

MyType source = new MyType();
Mapper.CreateMap<MyType, MyType>();
MyType target = Mapper.Map<MyType, MyType>(source);

L'oggetto di destinazione è ora una copia dell'oggetto di origine. Non abbastanza semplice? Creare un metodo di estensione da utilizzare ovunque nella soluzione:

public static T Copy<T>(this T source)
{
    T copy = default(T);
    Mapper.CreateMap<T, T>();
    copy = Mapper.Map<T, T>(source);
    return copy;
}

Utilizzando il metodo di estensione, le tre linee diventano una riga:

MyType copy = source.Copy();

Mi è venuto in mente questo per superare un problema di .NET che deve copiare manualmente la lista <T>.

Io uso questo:

static public IEnumerable<SpotPlacement> CloneList(List<SpotPlacement> spotPlacements)
{
    foreach (SpotPlacement sp in spotPlacements)
    {
        yield return (SpotPlacement)sp.Clone();
    }
}

E in un altro posto:

public object Clone()
{
    OrderItem newOrderItem = new OrderItem();
    ...
    newOrderItem._exactPlacements.AddRange(SpotPlacement.CloneList(_exactPlacements));
    ...
    return newOrderItem;
}

Ho provato a creare oneliner che lo fa, ma non è possibile, a causa del rendimento che non funziona all'interno di blocchi di metodi anonimi.

Meglio ancora, usa il cloner Elenco <T> generico:

class Utility<T> where T : ICloneable
{
    static public IEnumerable<T> CloneList(List<T> tl)
    {
        foreach (T t in tl)
        {
            yield return (T)t.Clone();
        }
    }
}




clone