2671 Stimmen

Tiefes Klonen von Objekten

Ich möchte etwas tun wie:

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

Und nehmen Sie dann Änderungen am neuen Objekt vor, die sich nicht im ursprünglichen Objekt widerspiegeln.

Ich brauche diese Funktionalität nicht oft, und wenn es nötig war, habe ich ein neues Objekt erstellt und dann jede Eigenschaft einzeln kopiert, aber das hinterlässt bei mir immer das Gefühl, dass es eine bessere oder elegantere Möglichkeit gibt, die Situation zu lösen.

Wie kann ich ein Objekt klonen oder tief kopieren, so dass das geklonte Objekt geändert werden kann, ohne dass sich die Änderungen auf das Originalobjekt auswirken?

112 Stimmen

Kann nützlich sein: "Warum ist das Kopieren eines Objekts eine schreckliche Sache?" agiledeveloper.com/articles/cloning072002.htm

2 Stimmen

stackoverflow.com/questions/8025890/ Eine andere Lösung...

28 Stimmen

Werfen Sie einen Blick auf AutoMapper

5voto

Michael Brown Punkte 1381

Da fast alle Antworten auf diese Frage unbefriedigend waren oder in meiner Situation einfach nicht funktionieren, habe ich einen Artikel verfasst AnyClone die vollständig mit Reflexion implementiert ist und alle hier gestellten Anforderungen erfüllt. Ich war nicht in der Lage, die Serialisierung in einem komplizierten Szenario mit komplexer Struktur zum Laufen zu bringen, und IClonable ist nicht gerade ideal - eigentlich sollte sie gar nicht nötig sein.

Standard-Ignore-Attribute werden unterstützt durch [IgnoreDataMember] , [NonSerialized] . Unterstützt komplexe Sammlungen, Eigenschaften ohne Setter, schreibgeschützte Felder usw.

Ich hoffe, es hilft jemandem da draußen, der auf die gleichen Probleme gestoßen ist wie ich.

0 Stimmen

Mit AnyClone Ich habe gerade das NuGet-Paket namens .Clone() installiert und es funktioniert sehr gut mit einem Blazor-Projekt hier!

5voto

Roma Borodov Punkte 511

Ok, es gibt einige offensichtliche Beispiele mit Reflexion in diesem Beitrag, ABER Reflexion ist normalerweise langsam, bis man anfängt, sie richtig zu cachen.

wenn Sie es richtig zwischenspeichern, dann werden 1000000 Objekte in 4,6s tief geklont (gemessen von Watcher).

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

als Sie nehmen zwischengespeicherte Eigenschaften oder fügen neue zum Wörterbuch hinzu und verwenden sie einfach

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

Vollständige Codeprüfung in meinem Beitrag in einer anderen Antwort

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

2 Stimmen

Aufruf von prop.GetValue(...) ist immer noch eine Reflexion und kann nicht zwischengespeichert werden. In einem Ausdrucksbaum ist es jedoch kompiliert, also schneller

4voto

Jeroen Ritmeijer Punkte 2791

Ich habe eine Version der akzeptierten Antwort erstellt, die sowohl mit '[Serializable]' als auch mit '[DataContract]' funktioniert. Es ist schon eine Weile her, seit ich sie geschrieben habe, aber wenn ich mich richtig erinnere, benötigte [DataContract] einen anderen Serialisierer.

Erfordert 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://stackoverflow.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;
    }

}

3voto

Chtiwi Malek Punkte 10464

Um Ihr Klassenobjekt zu klonen, können Sie die Methode Object.MemberwiseClone verwenden,

Fügen Sie einfach diese Funktion in Ihre Klasse ein:

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

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

Um dann eine tiefe, unabhängige Kopie durchzuführen, rufen Sie einfach die Methode DeepCopy auf:

yourClass newLine = oldLine.DeepCopy();

Ich hoffe, das hilft.

6 Stimmen

Die MemberwiseClone-Methode erstellt eine oberflächliche Kopie, NICHT eine tiefe Kopie. msdn.microsoft.com/de-us/library/

0 Stimmen

@odyth wichtiger Kommentar als aktueller Code Do shallow copy, Hier guter Artikel über Clone und Beispiele für jeden Typ geeksforgeeks.org/shallow-copy-and-deep-copy-in-c-sharp

0 Stimmen

Bis jetzt funktioniert das in meiner Situation. Ich danke Ihnen.

3voto

Sudhanva Kotabagi Punkte 166

Ich denke, Sie können dies versuchen.

MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = new MyObject(myObj); //DeepClone it

0 Stimmen

Zwei Probleme - erstens gibt es keinen automatischen Konstruktor in c#, der ein Objekt desselben Typs annimmt, also new MyObject(myObj); wird wahrscheinlich fehlschlagen. Zweitens, wenn Sie einen solchen Konstruktor erstellt haben, wäre es entweder ein flacher Klon oder Sie müssten ähnliche Konstruktoren bei jedem Schritt in die Eigenschaften des Objekts verwenden, um enthaltene Objekte und Sammlungen zu klonen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X