it-swarm-eu.dev

Tiefes Klonen von Objekten

Ich möchte etwas machen wie:

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

Nehmen Sie dann Änderungen am neuen Objekt vor, die nicht im ursprünglichen Objekt widergespiegelt werden.

Ich brauche diese Funktionalität nicht oft. Deshalb habe ich mich, wenn es nötig war, daran gemacht, ein neues Objekt zu erstellen und dann jede Eigenschaft einzeln zu kopieren. Ich habe jedoch immer das Gefühl, dass es eine bessere oder elegantere Art der Handhabung gibt die Situation.

Wie kann ich ein Objekt klonen oder tief kopieren, damit das geklonte Objekt geändert werden kann, ohne dass Änderungen im Originalobjekt übernommen werden?

2088
NakedBrunch

Während die Standardpraxis darin besteht, die Schnittstelle ICloneable (beschrieben hier zu implementieren, damit ich nicht nachgeben kann), ist hier ein netter Deep-Clone-Objektkopierer, den ich auf gefunden habe. Das Code-Projekt vor einer Weile und hat es in unser Zeug aufgenommen.

Wie an anderer Stelle erwähnt, müssen Ihre Objekte serialisierbar sein.

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.", nameof(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);
        }
    }
}

Die Idee ist, dass es Ihr Objekt serialisiert und es dann in ein neues Objekt deserialisiert. Der Vorteil ist, dass Sie sich nicht darum kümmern müssen, alles zu klonen, wenn ein Objekt zu komplex wird.

Und mit der Verwendung von Erweiterungsmethoden (auch aus der ursprünglich referenzierten Quelle):

Wenn Sie die neue Erweiterungsmethode von C # 3.0 verwenden möchten, ändern Sie die Methode so, dass sie die folgende Signatur aufweist:

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

Jetzt wird der Methodenaufruf einfach zu objectBeingCloned.Clone();.

EDIT (10. Januar 2015) Ich dachte, ich würde dies noch einmal überdenken, um zu erwähnen, dass ich kürzlich damit begonnen habe, (Newtonsoft) Json zu verwenden, um dies zu tun. sollte sein leichter und vermeidet den Overhead von [Serializable] -Tags. (NB @atconway hat in den Kommentaren darauf hingewiesen, dass private Mitglieder nicht mit der JSON-Methode geklont werden.)

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

Ich wollte einen Kloner für sehr einfache Objekte, die größtenteils aus Primitiven und Listen bestehen. Wenn Ihr Objekt standardmäßig mit JSON serialisierbar ist, reicht diese Methode aus. Dies erfordert keine Änderung oder Implementierung von Schnittstellen in der geklonten Klasse, nur einen JSON-Serialisierer wie JSON.NET.

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

Sie können diese Erweiterungsmethode auch verwenden

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

Der Grund, warum ICloneable nicht verwendet wird, ist nicht , da es keine generische Schnittstelle gibt. Der Grund, es nicht zu benutzen, ist, weil es vage ist . Es ist nicht klar, ob Sie eine flache oder eine tiefe Kopie erhalten. Das liegt beim Implementierer.

Ja, MemberwiseClone erstellt eine flache Kopie, aber das Gegenteil von MemberwiseClone ist nicht Clone; es wäre vielleicht DeepClone, das es nicht gibt. Wenn Sie ein Objekt über seine ICloneable-Schnittstelle verwenden, können Sie nicht wissen, welche Art von Klonen das zugrunde liegende Objekt ausführt. (Und XML-Kommentare machen dies nicht deutlich, da Sie die Schnittstellenkommentare und nicht die der Clone-Methode des Objekts erhalten.)

Normalerweise mache ich einfach eine Copy Methode, die genau das macht, was ich will.

169
Ryan Lundy

Nachdem ich viel über viele der hier verlinkten Optionen und mögliche Lösungen für dieses Problem gelesen habe, glaube ich alle Optionen sind unter Ian P 's Link ziemlich gut zusammengefasst (alle anderen Optionen sind Variationen davon) und die beste Lösung bietet Pedro77 's Link in den Fragenkommentaren.

Deshalb kopiere ich hier nur relevante Teile dieser 2 Referenzen. So können wir haben:

Das Beste, um Objekte in cis zu klonen!

Dies sind in erster Linie alle unsere Optionen:

Der Artikel Fast Deep Copy by Expression Trees bietet auch einen Leistungsvergleich des Klonens durch Serialization, Reflection und Expression Trees.

Warum ich ICloneable wähle (d. H. Manuell)

Herr Venkat Subramaniam (überflüssiger Link hier) erklärt ausführlich, warum .

Der gesamte Artikel umkreist ein Beispiel, das für die meisten Fälle anwendbar sein soll, und verwendet 3 Objekte: Person , Gehirn und Stadt . Wir wollen eine Person klonen, die ein eigenes Gehirn hat, aber dieselbe Stadt. Sie können sich alle Probleme vorstellen oder den Artikel lesen.

Dies ist meine leicht abgewandelte Version seiner Schlussfolgerung:

Das Kopieren eines Objekts durch Angabe von New gefolgt vom Klassennamen führt häufig zu Code, der nicht erweiterbar ist. Die Verwendung von Klonen, die Anwendung von Prototypmustern, ist ein besserer Weg, um dies zu erreichen. Die Verwendung von Clone, wie es in C # (und Java) bereitgestellt wird, kann jedoch ebenfalls problematisch sein. Es ist besser, einen geschützten (nicht öffentlichen) Kopierkonstruktor bereitzustellen und diesen über die Klonmethode aufzurufen. Dies gibt uns die Möglichkeit, die Aufgabe des Erstellens eines Objekts an eine Instanz einer Klasse selbst zu delegieren, wodurch eine Erweiterbarkeit bereitgestellt und die Objekte auch mit dem Konstruktor für geschützte Kopien sicher erstellt werden.

Hoffentlich kann diese Implementierung die Dinge klarstellen:

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

Betrachten Sie nun, eine Klasse von Person ableiten zu lassen.

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

Sie können versuchen, den folgenden Code auszuführen:

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

Das Ergebnis wird sein:

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]

Beachten Sie, dass der hier implementierte Klon die Anzahl der Objekte korrekt zählt, wenn die Anzahl der Objekte gezählt wird.

106
cregox

Ich bevorzuge einen Kopierkonstruktor einem Klon. Die Absicht ist klarer.

81
Nick

Einfache Erweiterungsmethode zum Kopieren aller öffentlichen Eigenschaften. Funktioniert für alle Objekte und nicht erfordert, dass die Klasse [Serializable] ist. Kann für andere Zugriffsebenen erweitert werden.

public static void CopyTo( this object S, object T )
{
    foreach( var pS in S.GetType().GetProperties() )
    {
        foreach( var pT in T.GetType().GetProperties() )
        {
            if( pT.Name != pS.Name ) continue;
            ( pT.GetSetMethod() ).Invoke( T, new object[] 
            { pS.GetGetMethod().Invoke( S, null ) } );
        }
    };
}
40

Nun, ich hatte Probleme bei der Verwendung von ICloneable in Silverlight, aber ich mochte die Idee der Seralisierung. Ich kann XML seralisieren, also habe ich Folgendes getan:

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);
    }
}
32
Michael White

Ich habe gerade ein CloneExtensions Bibliothek Projekt erstellt. Es führt ein schnelles, tiefgreifendes Klonen durch, indem einfache Zuweisungsoperationen verwendet werden, die durch die Kompilierung des Expression Tree-Laufzeitcodes generiert werden.

Wie benutzt man es?

Anstatt Ihre eigenen Clone - oder Copy -Methoden mit einem Ton von Zuweisungen zwischen Feldern und Eigenschaften zu schreiben, lässt das Programm dies mithilfe von Expression Tree selbst tun. Mit der als Erweiterungsmethode gekennzeichneten Methode GetClone<T>() können Sie sie einfach auf Ihrer Instanz aufrufen:

var newInstance = source.GetClone();

Sie können wählen, was von source nach newInstance kopiert werden soll, indem Sie CloningFlags enum verwenden:

var newInstance 
    = source.GetClone(CloningFlags.Properties | CloningFlags.CollectionItems);

Was kann geklont werden?

  • Primitive (int, uint, byte, double, char usw.), bekannte unveränderliche Typen (DateTime, TimeSpan, String) und Delegaten (einschließlich Action, Func usw.)
  • Nullable
  • T [] Arrays
  • Benutzerdefinierte Klassen und Strukturen, einschließlich generischer Klassen und Strukturen.

Folgende Klassen-/Strukturmember werden intern geklont:

  • Werte von öffentlichen, nicht schreibgeschützten Feldern
  • Werte von öffentlichen Eigenschaften mit get- und set-Accessoren
  • Auflistungselemente für Typen, die ICollection implementieren

Wie schnell ist es?

Die Lösung ist schneller als das Nachdenken, da Mitgliederinformationen nur einmal gesammelt werden müssen, bevor GetClone<T> zum ersten Mal für den angegebenen Typ T verwendet wird.

Es ist auch schneller als eine auf Serialisierung basierende Lösung, wenn Sie mehr als zwei Instanzen desselben Typs klonen T.

und mehr ...

Weitere Informationen zu generierten Ausdrücken finden Sie unter Dokumentation .

Beispiel für eine Ausdrucks-Debug-Auflistung für List<int>:

.Lambda #Lambda1<System.Func`4[System.Collections.Generic.List`1[System.Int32],CloneExtensions.CloningFlags,System.Collections.Generic.IDictionary`2[System.Type,System.Func`2[System.Object,System.Object]],System.Collections.Generic.List`1[System.Int32]]>(
    System.Collections.Generic.List`1[System.Int32] $source,
    CloneExtensions.CloningFlags $flags,
    System.Collections.Generic.IDictionary`2[System.Type,System.Func`2[System.Object,System.Object]] $initializers) {
    .Block(System.Collections.Generic.List`1[System.Int32] $target) {
        .If ($source == null) {
            .Return #Label1 { null }
        } .Else {
            .Default(System.Void)
        };
        .If (
            .Call $initializers.ContainsKey(.Constant<System.Type>(System.Collections.Generic.List`1[System.Int32]))
        ) {
            $target = (System.Collections.Generic.List`1[System.Int32]).Call ($initializers.Item[.Constant<System.Type>(System.Collections.Generic.List`1[System.Int32])]
            ).Invoke((System.Object)$source)
        } .Else {
            $target = .New System.Collections.Generic.List`1[System.Int32]()
        };
        .If (
            ((System.Byte)$flags & (System.Byte).Constant<CloneExtensions.CloningFlags>(Fields)) == (System.Byte).Constant<CloneExtensions.CloningFlags>(Fields)
        ) {
            .Default(System.Void)
        } .Else {
            .Default(System.Void)
        };
        .If (
            ((System.Byte)$flags & (System.Byte).Constant<CloneExtensions.CloningFlags>(Properties)) == (System.Byte).Constant<CloneExtensions.CloningFlags>(Properties)
        ) {
            .Block() {
                $target.Capacity = .Call CloneExtensions.CloneFactory.GetClone(
                    $source.Capacity,
                    $flags,
                    $initializers)
            }
        } .Else {
            .Default(System.Void)
        };
        .If (
            ((System.Byte)$flags & (System.Byte).Constant<CloneExtensions.CloningFlags>(CollectionItems)) == (System.Byte).Constant<CloneExtensions.CloningFlags>(CollectionItems)
        ) {
            .Block(
                System.Collections.Generic.IEnumerator`1[System.Int32] $var1,
                System.Collections.Generic.ICollection`1[System.Int32] $var2) {
                $var1 = (System.Collections.Generic.IEnumerator`1[System.Int32]).Call $source.GetEnumerator();
                $var2 = (System.Collections.Generic.ICollection`1[System.Int32])$target;
                .Loop  {
                    .If (.Call $var1.MoveNext() != False) {
                        .Call $var2.Add(.Call CloneExtensions.CloneFactory.GetClone(
                                $var1.Current,
                                $flags,


                         $initializers))
                } .Else {
                    .Break #Label2 { }
                }
            }
            .LabelTarget #Label2:
        }
    } .Else {
        .Default(System.Void)
    };
    .Label
        $target
    .LabelTarget #Label1:
}

}

was hat die gleiche Bedeutung wie C # -Code:

(source, flags, initializers) =>
{
    if(source == null)
        return null;

    if(initializers.ContainsKey(typeof(List<int>))
        target = (List<int>)initializers[typeof(List<int>)].Invoke((object)source);
    else
        target = new List<int>();

    if((flags & CloningFlags.Properties) == CloningFlags.Properties)
    {
        target.Capacity = target.Capacity.GetClone(flags, initializers);
    }

    if((flags & CloningFlags.CollectionItems) == CloningFlags.CollectionItems)
    {
        var targetCollection = (ICollection<int>)target;
        foreach(var item in (ICollection<int>)source)
        {
            targetCollection.Add(item.Clone(flags, initializers));
        }
    }

    return target;
}

Ist es nicht so, als würdest du deine eigene Clone Methode für List<int> schreiben?

29
MarcinJuraszek

Wenn Sie bereits eine Drittanbieteranwendung wie ValueInjecter oder Automapper verwenden, können Sie Folgendes tun:

MyObject oldObj; // The existing object to clone

MyObject newObj = new MyObject();
newObj.InjectFrom(oldObj); // Using ValueInjecter syntax

Mit dieser Methode müssen Sie ISerializable oder ICloneable nicht auf Ihren Objekten implementieren. Dies ist bei MVC/MVVM-Mustern üblich. Daher wurden einfache Tools wie dieses erstellt.

siehe die Valueinjecter Deep Cloning-Lösung auf CodePlex .

27
Michael Cox

Am besten implementieren Sie eine Erweiterungsmethode wie

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

und verwenden Sie es dann an einer beliebigen Stelle in der Lösung von

var copy = anyObject.DeepClone();

Wir können die folgenden drei Implementierungen haben:

  1. Durch Serialisierung (der kürzeste Code)
  2. Durch Reflektion - 5x schneller
  3. Durch Ausdrucksbäume - 20x schneller

Alle verknüpften Methoden funktionieren gut und wurden ausgiebig getestet.

21
frakon

Die kurze Antwort lautet, dass Sie von der ICloneable-Schnittstelle erben und dann die .clone-Funktion implementieren. Clone sollte eine memberweise Kopie erstellen und eine Deep Copy für jedes Member ausführen, für das dies erforderlich ist, und dann das resultierende Objekt zurückgeben. Dies ist eine rekursive Operation (es ist erforderlich, dass alle Mitglieder der Klasse, die Sie klonen möchten, entweder Werttypen sind oder ICloneable implementieren, und dass ihre Mitglieder entweder Werttypen sind oder ICloneable implementieren usw.).

Weitere Informationen zum Klonen mit ICloneable finden Sie in diesem Artikel .

Die lange Antwort lautet "es kommt darauf an". Wie bereits von anderen erwähnt, wird ICloneable von Generika nicht unterstützt, erfordert spezielle Überlegungen für zirkuläre Klassenverweise und wird von einigen als "Fehler" in .NET Framework angesehen. Die Serialisierungsmethode hängt davon ab, ob Ihre Objekte serialisierbar sind, was möglicherweise nicht der Fall ist und über die Sie möglicherweise keine Kontrolle haben. Es gibt immer noch viele Diskussionen in der Community über die "beste" Praxis. In Wirklichkeit ist keine der Lösungen die Einheitslösung für alle Situationen, für die ICloneable ursprünglich ausgelegt wurde.

Weitere Optionen finden Sie im Artikel this Entwicklerecke (Dank an Ian).

21
Zach Burlingame
  1. Grundsätzlich müssen Sie die ICloneable-Schnittstelle implementieren und dann das Kopieren der Objektstruktur durchführen.
  2. Wenn es sich um eine umfangreiche Kopie aller Mitglieder handelt, müssen Sie sicherstellen (unabhängig von der gewählten Lösung), dass auch alle Kinder klonbar sind.
  3. Manchmal müssen Sie sich während dieses Vorgangs einer Einschränkung bewusst sein. Wenn Sie beispielsweise die ORM-Objekte kopieren, lassen die meisten Frameworks nur ein Objekt zu, das an die Sitzung angehängt ist, und Sie DÜRFEN KEINE Klone dieses Objekts erstellen über die Sitzungsanfügung dieser Objekte.

Prost.

17
dimarzionist

Wenn Sie echtes Klonen auf unbekannte Typen wollen, schauen Sie sich fastclone an.

Das expression-basierte Klonen arbeitet etwa zehnmal schneller als die binäre Serialisierung und behält die vollständige Integrität des Objektgraphen bei.

Das bedeutet: Wenn Sie in Ihrer Hierarchie mehrmals auf dasselbe Objekt verweisen, wird auf den Klon auch eine einzelne Instanz verwiesen.

Es sind keine Schnittstellen, Attribute oder sonstigen Änderungen an den zu klonenden Objekten erforderlich.

16
Michael Sander

Halten Sie die Dinge einfach und verwenden Sie AutoMapper , wie bereits erwähnt, eine einfache kleine Bibliothek, um ein Objekt einem anderen zuzuordnen ... Um ein Objekt mit demselben Typ in ein anderes zu kopieren, benötigen Sie nur drei Codezeilen :

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

Das Zielobjekt ist jetzt eine Kopie des Quellobjekts. Nicht einfach genug? Erstellen Sie eine Erweiterungsmethode, die überall in Ihrer Lösung verwendet werden kann:

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

Bei Verwendung der Erweiterungsmethode werden die drei Zeilen zu einer Zeile:

MyType copy = source.Copy();
12
Stacked

Ich habe mir das ausgedacht, um ein . NET Manko zu überwinden, das darin besteht, die Liste <T> manuell tief zu kopieren.

Ich benutze das:

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

Und an einem anderen Ort:

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

Ich habe versucht, einen Oneliner zu finden, der dies tut, aber es ist nicht möglich, da die Ausgabe in anonymen Methodenblöcken nicht funktioniert.

Verwenden Sie noch besser den generischen List <T> Cloner:

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

F. Warum sollte ich diese Antwort wählen?

  • Wählen Sie diese Antwort, wenn Sie die schnellste Geschwindigkeit wünschen, die .NET erreichen kann.
  • Ignorieren Sie diese Antwort, wenn Sie eine wirklich einfache Methode zum Klonen möchten.

Mit anderen Worten: Geben Sie eine andere Antwort, es sei denn, Sie haben einen Leistungsengpass, der behoben werden muss, und Sie können dies mit einem Profiler beweisen. .

10x schneller als andere Methoden

Die folgende Methode zum Ausführen eines tiefen Klons ist:

  • 10x schneller als alles, was Serialisierung/Deserialisierung beinhaltet;
  • Ziemlich nahe an der theoretischen Höchstgeschwindigkeit, zu der .NET fähig ist.

Und die Methode ...

Für die ultimative Geschwindigkeit können Sie Nested MemberwiseClone zum Erstellen einer tiefen Kopie verwenden. Es ist fast so schnell wie das Kopieren einer Wertestruktur und viel schneller als (a) Reflektion oder (b) Serialisierung (wie in den anderen Antworten auf dieser Seite beschrieben).

Beachten Sie, dass if Sie Nested MemberwiseClone für eine tiefe Kopie verwenden, Sie eine ShallowCopy für jede verschachtelte Ebene in der Klasse manuell implementieren müssen, und eine DeepCopy, die alle besagten ShallowCopy-Methoden aufruft um einen vollständigen Klon zu erstellen. Das ist ganz einfach: Nur ein paar Zeilen insgesamt, siehe den Demo-Code unten.

Hier ist die Ausgabe des Codes, die den relativen Leistungsunterschied für 100.000 Klone zeigt:

  • 1,08 Sekunden für Nested MemberwiseClone auf verschachtelten Strukturen
  • 4,77 Sekunden für Nested MemberwiseClone in verschachtelten Klassen
  • 39,93 Sekunden für Serialisierung/Deserialisierung

Die Verwendung von Nested MemberwiseClone in einer Klasse ist fast so schnell wie das Kopieren einer Struktur und das Kopieren einer Struktur entspricht in etwa der theoretischen Höchstgeschwindigkeit, die .NET erreichen kann.

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

Um zu verstehen, wie mit MemberwiseCopy eine vertiefte Kopie erstellt wird, finden Sie hier das Demo-Projekt, mit dem die oben genannten Zeiten generiert wurden:

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

Rufen Sie dann die Demo von main auf:

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

Beachten Sie erneut, dass if Sie Nested MemberwiseClone für eine tiefe Kopie verwenden, Sie müssen manuell eine ShallowCopy für jede verschachtelte Ebene in der Klasse implementieren und eine DeepCopy, die alle genannten aufruft ShallowCopy-Methoden zum Erstellen eines vollständigen Klons. Das ist ganz einfach: Nur ein paar Zeilen insgesamt, siehe obigen Demo-Code.

Werttypen vs. Referenztypen

Beachten Sie, dass beim Klonen eines Objekts ein großer Unterschied zwischen "struct" und "class" besteht:

  • Wenn Sie ein "struct" haben, ist es ein Werttyp, sodass Sie es einfach kopieren können, und der Inhalt wird geklont (es wird jedoch nur ein flacher Klon erstellt, sofern Sie dies nicht tun Verwenden Sie die Techniken in diesem Beitrag).
  • Wenn Sie ein "class" haben, ist es ein reference type. Wenn Sie es also kopieren, kopieren Sie nur den Zeiger darauf. Um einen echten Klon zu erstellen, müssen Sie kreativer sein und nterschiede zwischen Werttypen und Referenztypen verwenden, um eine weitere Kopie des ursprünglichen Objekts im Speicher zu erstellen.

Siehe nterschiede zwischen Werttypen und Referenztypen .

Prüfsummen zur Unterstützung beim Debuggen

  • Falsches Klonen von Objekten kann zu sehr schwer zu lokalisierenden Fehlern führen. Im Produktionscode implementiere ich in der Regel eine Prüfsumme, um zu überprüfen, ob das Objekt ordnungsgemäß geklont und nicht durch einen anderen Verweis darauf beschädigt wurde. Diese Prüfsumme kann im Freigabemodus abgeschaltet werden.
  • Ich finde diese Methode sehr nützlich: Oft möchten Sie nur Teile des Objekts klonen, nicht das gesamte Objekt.

Sehr nützlich zum Entkoppeln vieler Threads von vielen anderen Threads

Ein ausgezeichneter Anwendungsfall für diesen Code ist das Einfügen von Klonen einer verschachtelten Klasse oder Struktur in eine Warteschlange, um das Producer/Consumer-Muster zu implementieren.

  • Wir können einen (oder mehrere) Threads haben, die eine Klasse ändern, deren Eigentümer sie sind, und dann eine vollständige Kopie dieser Klasse in ein ConcurrentQueue verschieben.
  • Wir haben dann einen (oder mehrere) Threads, die Kopien dieser Klassen herausziehen und sich mit ihnen befassen.

Dies funktioniert in der Praxis sehr gut und ermöglicht es uns, viele Threads (die Produzenten) von einem oder mehreren Threads (den Konsumenten) zu entkoppeln.

Und diese Methode ist erstaunlich schnell: Wenn wir verschachtelte Strukturen verwenden, ist sie 35-mal schneller als das Serialisieren/Deserialisieren verschachtelter Klassen und ermöglicht es uns, alle auf der Maschine verfügbaren Threads zu nutzen.

Aktualisieren

Anscheinend ist ExpressMapper genauso schnell, wenn nicht sogar schneller als die oben beschriebene Handcodierung. Ich muss vielleicht sehen, wie sie mit einem Profiler vergleichen.

9
Contango

Ich habe gesehen, wie es auch durch Reflektion umgesetzt wurde. Grundsätzlich gab es eine Methode, mit der die Elemente eines Objekts durchlaufen und in geeigneter Weise in das neue Objekt kopiert wurden. Als es Referenztypen oder Sammlungen erreichte, hat es sich meiner Meinung nach selbst rekursiv aufgerufen. Reflexion ist teuer, hat aber ganz gut funktioniert.

8
xr280xr

Da ich in verschiedenen Projekten keinen Kloner gefunden habe, der alle meine Anforderungen erfüllt, habe ich einen Deep-Cloner erstellt, der konfiguriert und an verschiedene Codestrukturen angepasst werden kann, anstatt meinen Code an die Anforderungen der Kloner anzupassen. Dies wird erreicht, indem Sie dem Code Anmerkungen hinzufügen, die geklont werden sollen, oder indem Sie den Code so lassen, wie er sein soll, um das Standardverhalten zu erhalten. Es verwendet Reflektion, gibt Caches ein und basiert auf quickflect . Der Klonprozess ist sehr schnell für eine große Datenmenge und eine hohe Objekthierarchie (im Vergleich zu anderen Algorithmen, die auf Reflexion/Serialisierung basieren).

https://github.com/kalisohn/CloneBehave

Auch als Nuget-Paket erhältlich: https://www.nuget.org/packages/Clone.Behave/1.0.

Beispiel: Mit dem folgenden Code wird deepClone Address ausgeführt, jedoch nur eine flache Kopie des Felds _currentJob.

public class Person 
{
  [DeepClone(DeepCloneBehavior.Shallow)]
  private Job _currentJob;      

  public string Name { get; set; }

  public Job CurrentJob 
  { 
    get{ return _currentJob; }
    set{ _currentJob = value; }
  }

  public Person Manager { get; set; }
}

public class Address 
{      
  public Person PersonLivingHere { get; set; }
}

Address adr = new Address();
adr.PersonLivingHere = new Person("John");
adr.PersonLivingHere.BestFriend = new Person("James");
adr.PersonLivingHere.CurrentJob = new Job("Programmer");

Address adrClone = adr.Clone();

//RESULT
adr.PersonLivingHere == adrClone.PersonLivingHere //false
adr.PersonLivingHere.Manager == adrClone.PersonLivingHere.Manager //false
adr.PersonLivingHere.CurrentJob == adrClone.PersonLivingHere.CurrentJob //true
adr.PersonLivingHere.CurrentJob.AnyProperty == adrClone.PersonLivingHere.CurrentJob.AnyProperty //true
8
kalisohn

Im Allgemeinen implementieren Sie die ICloneable-Schnittstelle und implementieren Clone selbst. C # -Objekte verfügen über eine integrierte MemberwiseClone-Methode, die eine flache Kopie ausführt, die Ihnen bei allen Grundelementen helfen kann.

Für eine vertiefte Kopie gibt es keine Möglichkeit, zu wissen, wie dies automatisch durchgeführt wird.

8
HappyDude

Hier ist eine Deep Copy-Implementierung:

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;
}
8
dougajmcdonald

Diese Methode hat das Problem für mich gelöst:

private static MyObj DeepCopy(MyObj source)
        {

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

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

        }

Verwenden Sie es wie folgt: MyObj a = DeepCopy(b);

7
JerryGoyal

Ich denke, Sie können das versuchen.

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

Ich mag solche Copyconstructors:

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

Wenn Sie mehr Dinge zum Kopieren haben, fügen Sie sie hinzu

6
LuckyLikey

Code Generator

Wir haben viele Ideen gesehen, von der Serialisierung über die manuelle Implementierung bis hin zur Reflexion, und ich möchte mit dem CGbR Code Generator einen völlig anderen Ansatz vorschlagen. Die Methode zum Generieren von Klonen ist speicher- und CPU-effizient und daher 300-mal schneller als der Standard-DataContractSerializer.

Alles was Sie brauchen ist eine partielle Klassendefinition mit ICloneable und der Generator erledigt den Rest:

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

Hinweis: Die neueste Version hat mehr Null-Checks, aber ich habe sie zum besseren Verständnis weggelassen.

6
Toxantron

Hier eine schnelle und einfache Lösung, die für mich funktioniert hat, ohne auf Serialisierung/Deserialisierung zu setzen.

public class MyClass
{
    public virtual MyClass DeepClone()
    {
        var returnObj = (MyClass)MemberwiseClone();
        var type = returnObj.GetType();
        var fieldInfoArray = type.GetRuntimeFields().ToArray();

        foreach (var fieldInfo in fieldInfoArray)
        {
            object sourceFieldValue = fieldInfo.GetValue(this);
            if (!(sourceFieldValue is MyClass))
            {
                continue;
            }

            var sourceObj = (MyClass)sourceFieldValue;
            var clonedObj = sourceObj.DeepClone();
            fieldInfo.SetValue(returnObj, clonedObj);
        }
        return returnObj;
    }
}

EDIT: erfordert

    using System.Linq;
    using System.Reflection;

So habe ich es benutzt

public MyClass Clone(MyClass theObjectIneededToClone)
{
    MyClass clonedObj = theObjectIneededToClone.DeepClone();
}
6
Daniele D.

Folge diesen Schritten:

  • Definieren Sie einen ISelf<T> mit einer schreibgeschützten Self -Eigenschaft, die T und ICloneable<out T> zurückgibt, die von ISelf<T> abgeleitet sind und eine Methode T Clone() enthalten. .
  • Definieren Sie dann einen CloneBase -Typ, der ein protected virtual generic VirtualClone-Casting MemberwiseClone für den übergebenen Typ implementiert.
  • Jeder abgeleitete Typ sollte VirtualClone implementieren, indem er die Basisklonmethode aufruft und dann alle erforderlichen Aktionen ausführt, um die Aspekte des abgeleiteten Typs ordnungsgemäß zu klonen, die die übergeordnete VirtualClone-Methode noch nicht behandelt hat.

Für eine maximale Vielseitigkeit der Vererbung sollten Klassen, die öffentliche Klonfunktionen bereitstellen, sealed sein, aber von einer Basisklasse abgeleitet werden, die ansonsten identisch ist, mit Ausnahme des Fehlens des Klonens. Verwenden Sie einen Parameter vom Typ ICloneable<theNonCloneableType>, anstatt Variablen vom expliziten klonbaren Typ zu übergeben. Dies ermöglicht einer Routine, die erwartet, dass ein klonbares Derivat von Foo mit einem klonbaren Derivat von DerivedFoo arbeitet, aber auch die Erstellung nicht klonbarer Derivate von Foo.

5
supercat

Ich habe eine Version der akzeptierten Antwort erstellt, die sowohl mit "[Serializable]" als auch mit "[DataContract]" funktioniert. Es ist schon eine Weile her, dass ich es geschrieben habe, aber wenn ich mich richtig erinnere, brauchte [DataContract] einen anderen Serializer.

Benötigt 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;
    }

} 
4

Wenn Ihr Objektbaum serialisierbar ist, können Sie auch so etwas verwenden

static public MyClass Clone(MyClass myClass)
{
    MyClass clone;
    XmlSerializer ser = new XmlSerializer(typeof(MyClass), _xmlAttributeOverrides);
    using (var ms = new MemoryStream())
    {
        ser.Serialize(ms, myClass);
        ms.Position = 0;
        clone = (MyClass)ser.Deserialize(ms);
    }
    return clone;
}

seien Sie sich darüber im Klaren, dass diese Lösung recht einfach ist, aber nicht so leistungsfähig wie andere Lösungen.

Und stellen Sie sicher, dass, wenn die Klasse wächst, immer noch nur die Felder geklont werden, die auch serialisiert werden.

4
LuckyLikey

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

füge einfach diese Funktion zu deiner Klasse hinzu:

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

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

rufen Sie anschließend die DeepCopy-Methode auf, um eine unabhängige Tiefenkopie durchzuführen:

yourClass newLine = oldLine.DeepCopy();

hoffe das hilft.

4
Chtiwi Malek

Ok, es gibt einige offensichtliche Beispiele für Reflexion in diesem Beitrag, ABER Reflexion ist normalerweise langsam, bis Sie anfangen, es richtig zu cachen.

wenn Sie es richtig zwischenspeichern, wird das 1000000-Objekt um 4,6s (gemessen von Watcher) tief geklont.

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

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

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

vollständiger Code in meinem Beitrag in einer anderen Antwort überprüfen

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

4
Roma Borodov

Da fast alle Antworten auf diese Frage unbefriedigend waren oder in meiner Situation eindeutig nicht funktionieren, habe ich AnyClone verfasst, das vollständig reflektiert implementiert und alle Anforderungen hier gelöst wird. Ich konnte die Serialisierung in einem komplizierten Szenario mit komplexer Struktur nicht zum Laufen bringen, und IClonable ist weniger als ideal - eigentlich sollte es nicht einmal notwendig sein.

Standardmäßige Ignorierattribute werden mit [IgnoreDataMember], [NonSerialized] unterstützt. 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, die ich gemacht habe.

3
Michael Brown

Wenn Sie Marc Gravells protobuf-net als Serializer verwenden, muss die akzeptierte Antwort geringfügig geändert werden, da das zu kopierende Objekt nicht mit [Serializable] gekennzeichnet wird und daher nicht serialisierbar ist und die Clone-Methode eine auslöst Ausnahme.
Ich habe es so modifiziert, dass es mit protobuf-net funktioniert:

public static T Clone<T>(this T source)
{
    if(Attribute.GetCustomAttribute(typeof(T), typeof(ProtoBuf.ProtoContractAttribute))
           == null)
    {
        throw new ArgumentException("Type has no ProtoContract!", "source");
    }

    if(Object.ReferenceEquals(source, null))
    {
        return default(T);
    }

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

Dadurch wird das Vorhandensein eines [ProtoContract] -Attributs überprüft und das Objekt mit dem eigenen Formatierungsprogramm von protobuf serialisiert.

2
Basti M

Die generischen Ansätze sind alle technisch gültig, aber ich wollte nur eine Anmerkung von mir hinzufügen, da wir selten wirklich eine echte Tiefenkopie benötigen, und ich würde der Verwendung der generischen Tiefenkopie in tatsächlichen Geschäftsanwendungen stark widersprechen, da dies dazu führt, dass Sie möglicherweise viele haben Stellen, an denen die Objekte kopiert und dann explizit geändert werden, können leicht verloren gehen.

In den meisten realen Situationen möchten Sie außerdem eine möglichst genaue Kontrolle über den Kopiervorgang haben, da Sie nicht nur an das Datenzugriffsframework gekoppelt sind, sondern auch in der Praxis die kopierten Geschäftsobjekte selten zu 100% identisch sein sollten. Angenommen, eine Referenz-ID, die vom ORM zum Identifizieren von Objektreferenzen verwendet wird, kopiert diese ID auch vollständig. Während sich die Objekte im Speicher befinden, werden sie unterschiedlich sein. Sobald Sie sie an den Datenspeicher senden, werden Sie sich beschweren müssen diese Eigenschaften nach dem Kopieren ohnehin manuell ändern, und wenn sich das Objekt ändert, müssen Sie es an allen Stellen anpassen, die das generische Tiefenkopieren verwenden.

Erweitern Sie @cregox answer mit ICloneable. Was ist eigentlich eine Deep Copy? Es ist nur ein neu zugewiesenes Objekt auf dem Heap, das mit dem ursprünglichen Objekt identisch ist, jedoch einen anderen Speicherplatz belegt, anstatt eine generische Cloner-Funktionalität zu verwenden. Warum nicht einfach ein neues Objekt erstellen?

Ich persönlich verwende die Idee statischer Factory-Methoden für meine Domain-Objekte.

Beispiel:

    public class Client
    {
        public string Name { get; set; }

        protected Client()
        {
        }

        public static Client Clone(Client copiedClient)
        {
            return new Client
            {
                Name = copiedClient.Name
            };
        }
    }

    public class Shop
    {
        public string Name { get; set; }

        public string Address { get; set; }

        public ICollection<Client> Clients { get; set; }

        public static Shop Clone(Shop copiedShop, string newAddress, ICollection<Client> clients)
        {
            var copiedClients = new List<Client>();
            foreach (var client in copiedShop.Clients)
            {
                copiedClients.Add(Client.Clone(client));
            }

            return new Shop
            {
                Name = copiedShop.Name,
                Address = newAddress,
                Clients = copiedClients
            };
        }
    }

Wenn jemand sucht, wie er Objektinstanziierung strukturieren kann, während er die volle Kontrolle über den Kopierprozess behält, ist dies eine Lösung, mit der ich persönlich sehr erfolgreich war. Die geschützten Konstruktoren machen es auch so, dass andere Entwickler gezwungen sind, die Factory-Methoden zu verwenden, die einen sauberen Punkt der Objektinstanziierung ergeben, der die Konstruktionslogik innerhalb des Objekts einkapselt. Sie können die Methode auch überladen und bei Bedarf mehrere Klonlogiken für verschiedene Stellen verwenden.

2

C # -Erweiterung, die auch die Typen "not ISerializable" unterstützt.

 public static class AppExtensions
 {                                                                      
       public static T DeepClone<T>(this T a)
       {
           using (var stream = new MemoryStream())
           {
               var serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));

               serializer.Serialize(stream, a);
               stream.Position = 0;
               return (T)serializer.Deserialize(stream);
           }
       }                                                                    
 }

Verwendungszweck

       var obj2 = obj1.DeepClone()
2
Sameera R.

Es ist unglaublich, wie viel Aufwand Sie mit der IClonable-Schnittstelle betreiben können - insbesondere, wenn Sie über umfangreiche Klassenhierarchien verfügen. Auch MemberwiseClone funktioniert irgendwie seltsam - es klont nicht gerade normale Strukturen vom Typ List.

Das interessanteste Dilemma für die Serialisierung ist natürlich die Serialisierung von Rückverweisen - z. Klassenhierarchien, in denen Sie Kind-Eltern-Beziehungen haben. Ich bezweifle, dass der binäre Serializer Ihnen in diesem Fall helfen kann. (Es wird mit rekursiven Schleifen + Stapelüberlauf enden).

Mir hat die hier vorgeschlagene Lösung irgendwie gefallen: Wie erstellt man eine tiefe Kopie eines Objekts in .NET (C # -spezifisch)?

es wurden jedoch keine Listen unterstützt. Diese Unterstützung berücksichtigte auch die erneute Elternschaft. Für die Elternschaft sollte nur die Regel, die ich für dieses Feld oder diese Eigenschaft erstellt habe, den Namen "parent" haben, dann wird sie von DeepClone ignoriert. Möglicherweise möchten Sie Ihre eigenen Regeln für Rückverweise festlegen - für Baumhierarchien kann dies "left/right" usw. sein.

Hier ist ein ganzer Codeausschnitt inklusive Testcode:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;

namespace TestDeepClone
{
    class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            a.name = "main_A";
            a.b_list.Add(new B(a) { name = "b1" });
            a.b_list.Add(new B(a) { name = "b2" });

            A a2 = (A)a.DeepClone();
            a2.name = "second_A";

            // Perform re-parenting manually after deep copy.
            foreach( var b in a2.b_list )
                b.parent = a2;


            Debug.WriteLine("ok");

        }
    }

    public class A
    {
        public String name = "one";
        public List<String> list = new List<string>();
        public List<String> null_list;
        public List<B> b_list = new List<B>();
        private int private_pleaseCopyMeAsWell = 5;

        public override string ToString()
        {
            return "A(" + name + ")";
        }
    }

    public class B
    {
        public B() { }
        public B(A _parent) { parent = _parent; }
        public A parent;
        public String name = "two";
    }


    public static class ReflectionEx
    {
        public static Type GetUnderlyingType(this MemberInfo member)
        {
            Type type;
            switch (member.MemberType)
            {
                case MemberTypes.Field:
                    type = ((FieldInfo)member).FieldType;
                    break;
                case MemberTypes.Property:
                    type = ((PropertyInfo)member).PropertyType;
                    break;
                case MemberTypes.Event:
                    type = ((EventInfo)member).EventHandlerType;
                    break;
                default:
                    throw new ArgumentException("member must be if type FieldInfo, PropertyInfo or EventInfo", "member");
            }
            return Nullable.GetUnderlyingType(type) ?? type;
        }

        /// <summary>
        /// Gets fields and properties into one array.
        /// Order of properties / fields will be preserved in order of appearance in class / struct. (MetadataToken is used for sorting such cases)
        /// </summary>
        /// <param name="type">Type from which to get</param>
        /// <returns>array of fields and properties</returns>
        public static MemberInfo[] GetFieldsAndProperties(this Type type)
        {
            List<MemberInfo> fps = new List<MemberInfo>();
            fps.AddRange(type.GetFields());
            fps.AddRange(type.GetProperties());
            fps = fps.OrderBy(x => x.MetadataToken).ToList();
            return fps.ToArray();
        }

        public static object GetValue(this MemberInfo member, object target)
        {
            if (member is PropertyInfo)
            {
                return (member as PropertyInfo).GetValue(target, null);
            }
            else if (member is FieldInfo)
            {
                return (member as FieldInfo).GetValue(target);
            }
            else
            {
                throw new Exception("member must be either PropertyInfo or FieldInfo");
            }
        }

        public static void SetValue(this MemberInfo member, object target, object value)
        {
            if (member is PropertyInfo)
            {
                (member as PropertyInfo).SetValue(target, value, null);
            }
            else if (member is FieldInfo)
            {
                (member as FieldInfo).SetValue(target, value);
            }
            else
            {
                throw new Exception("destinationMember must be either PropertyInfo or FieldInfo");
            }
        }

        /// <summary>
        /// Deep clones specific object.
        /// Analogue can be found here: https://stackoverflow.com/questions/129389/how-do-you-do-a-deep-copy-an-object-in-net-c-specifically
        /// This is now improved version (list support added)
        /// </summary>
        /// <param name="obj">object to be cloned</param>
        /// <returns>full copy of object.</returns>
        public static object DeepClone(this object obj)
        {
            if (obj == null)
                return null;

            Type type = obj.GetType();

            if (obj is IList)
            {
                IList list = ((IList)obj);
                IList newlist = (IList)Activator.CreateInstance(obj.GetType(), list.Count);

                foreach (object elem in list)
                    newlist.Add(DeepClone(elem));

                return newlist;
            } //if

            if (type.IsValueType || type == typeof(string))
            {
                return obj;
            }
            else if (type.IsArray)
            {
                Type elementType = Type.GetType(type.FullName.Replace("[]", string.Empty));
                var array = obj as Array;
                Array copied = Array.CreateInstance(elementType, array.Length);

                for (int i = 0; i < array.Length; i++)
                    copied.SetValue(DeepClone(array.GetValue(i)), i);

                return Convert.ChangeType(copied, obj.GetType());
            }
            else if (type.IsClass)
            {
                object toret = Activator.CreateInstance(obj.GetType());

                MemberInfo[] fields = type.GetFieldsAndProperties();
                foreach (MemberInfo field in fields)
                {
                    // Don't clone parent back-reference classes. (Using special kind of naming 'parent' 
                    // to indicate child's parent class.
                    if (field.Name == "parent")
                    {
                        continue;
                    }

                    object fieldValue = field.GetValue(obj);

                    if (fieldValue == null)
                        continue;

                    field.SetValue(toret, DeepClone(fieldValue));
                }

                return toret;
            }
            else
            {
                // Don't know that type, don't know how to clone it.
                if (Debugger.IsAttached)
                    Debugger.Break();

                return null;
            }
        } //DeepClone
    }

}
2
TarmoPikaro

Noch eine andere JSON.NET-Antwort. Diese Version funktioniert mit Klassen, die ISerializable nicht implementieren.

public static class Cloner
{
    public static T Clone<T>(T source)
    {
        if (ReferenceEquals(source, null))
            return default(T);

        var settings = new JsonSerializerSettings { ContractResolver = new ContractResolver() };

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

    class ContractResolver : DefaultContractResolver
    {
        protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
        {
            var props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                .Select(p => base.CreateProperty(p, memberSerialization))
                .Union(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                    .Select(f => base.CreateProperty(f, memberSerialization)))
                .ToList();
            props.ForEach(p => { p.Writable = true; p.Readable = true; });
            return props;
        }
    }
}
2
Matthew Watson

Ein Mapper führt eine Tiefenkopie durch. Für jedes Mitglied Ihres Objekts erstellt es ein neues Objekt und weist alle seine Werte zu. Es funktioniert rekursiv auf jedem nicht-primitiven inneren Element.

Ich schlage Ihnen eine der schnellsten, derzeit aktiv entwickelten vor. Ich empfehle UltraMapper https://github.com/maurosampietro/UltraMapper

Nuget-Pakete: https://www.nuget.org/packages/UltraMapper/

2
Mauro Sampietro

Ich habe einen neuen Weg gefunden, Emit.

Wir können Emit verwenden, um die IL zur App hinzuzufügen und sie auszuführen. Aber ich denke nicht, dass es ein guter Weg ist, um dies zu perfektionieren, indem ich meine Antwort schreibe.

Der Emit kann das offizielles Dokument und Führer sehen

Sie sollten etwas IL lernen, um den Code zu lesen. Ich werde den Code schreiben, der die Eigenschaft in der Klasse kopieren kann.

public static class Clone
{        
    // ReSharper disable once InconsistentNaming
    public static void CloneObjectWithIL<T>(T source, T los)
    {
        //see http://lindexi.oschina.io/lindexi/post/C-%E4%BD%BF%E7%94%A8Emit%E6%B7%B1%E5%85%8B%E9%9A%86/
        if (CachedIl.ContainsKey(typeof(T)))
        {
            ((Action<T, T>) CachedIl[typeof(T)])(source, los);
            return;
        }
        var dynamicMethod = new DynamicMethod("Clone", null, new[] { typeof(T), typeof(T) });
        ILGenerator generator = dynamicMethod.GetILGenerator();

        foreach (var temp in typeof(T).GetProperties().Where(temp => temp.CanRead && temp.CanWrite))
        {
            //do not copy static that will except
            if (temp.GetAccessors(true)[0].IsStatic)
            {
                continue;
            }

            generator.Emit(OpCodes.Ldarg_1);// los
            generator.Emit(OpCodes.Ldarg_0);// s
            generator.Emit(OpCodes.Callvirt, temp.GetMethod);
            generator.Emit(OpCodes.Callvirt, temp.SetMethod);
        }
        generator.Emit(OpCodes.Ret);
        var clone = (Action<T, T>) dynamicMethod.CreateDelegate(typeof(Action<T, T>));
        CachedIl[typeof(T)] = clone;
        clone(source, los);
    }

    private static Dictionary<Type, Delegate> CachedIl { set; get; } = new Dictionary<Type, Delegate>();
}

Der Code kann eine tiefe Kopie sein, aber die Eigenschaft kann kopiert werden. Wenn Sie es zu einer tiefen Kopie machen wollen, können Sie es für die IL ändern, es ist zu schwer, dass ich es nicht tun kann.

1
lindexi

wie wäre es, wenn Sie nur eine Methode neu zusammenstellen, die im Grunde genommen einen automatischen Kopierkonstruktor aufrufen sollte

T t = new T();
T t2 = (T)t;  //eh something like that

        List<myclass> cloneum;
        public void SomeFuncB(ref List<myclass> _mylist)
        {
            cloneum = new List<myclass>();
            cloneum = (List < myclass >) _mylist;
            cloneum.Add(new myclass(3));
            _mylist = new List<myclass>();
        }

scheint mir zu funktionieren

1
will_m

Beim tiefen Klonen geht es um das Kopieren Zustand. Für .netZustand bedeutet Felder.

Nehmen wir an, man hat eine Hierarchie:

static class RandomHelper
{
    private static readonly Random random = new Random();

    public static int Next(int maxValue) => random.Next(maxValue);
}

class A
{
    private readonly int random = RandomHelper.Next(100);

    public override string ToString() => $"{typeof(A).Name}.{nameof(random)} = {random}";
}

class B : A
{
    private readonly int random = RandomHelper.Next(100);

    public override string ToString() => $"{typeof(B).Name}.{nameof(random)} = {random} {base.ToString()}";
}

class C : B
{
    private readonly int random = RandomHelper.Next(100);

    public override string ToString() => $"{typeof(C).Name}.{nameof(random)} = {random} {base.ToString()}";
}

Das Klonen kann erfolgen:

static class DeepCloneExtension
{
    // consider instance fields, both public and non-public
    private static readonly BindingFlags bindingFlags =
        BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;

    public static T DeepClone<T>(this T obj) where T : new()
    {
        var type = obj.GetType();
        var result = (T)Activator.CreateInstance(type);

        do
            // copy all fields
            foreach (var field in type.GetFields(bindingFlags))
                field.SetValue(result, field.GetValue(obj));
        // for every level of hierarchy
        while ((type = type.BaseType) != typeof(object));

        return result;
    }
}

Demo1:

Console.WriteLine(new C());
Console.WriteLine(new C());

var c = new C();
Console.WriteLine($"{Environment.NewLine}Image: {c}{Environment.NewLine}");

Console.WriteLine(new C());
Console.WriteLine(new C());

Console.WriteLine($"{Environment.NewLine}Clone: {c.DeepClone()}{Environment.NewLine}");

Console.WriteLine(new C());
Console.WriteLine(new C());

Ergebnis:

C.random = 92 B.random = 66 A.random = 71
C.random = 36 B.random = 64 A.random = 17

Image: C.random = 96 B.random = 18 A.random = 46

C.random = 60 B.random = 7 A.random = 37
C.random = 78 B.random = 11 A.random = 18

Clone: C.random = 96 B.random = 18 A.random = 46

C.random = 33 B.random = 63 A.random = 38
C.random = 4 B.random = 5 A.random = 79

Beachten Sie, dass alle neuen Objekte Zufallswerte für das Feld random haben, aber clone genau mit image übereinstimmt.

Demo2:

class D
{
    public event EventHandler Event;
    public void RaiseEvent() => Event?.Invoke(this, EventArgs.Empty);
}

// ...

var image = new D();
Console.WriteLine($"Created obj #{image.GetHashCode()}");

image.Event += (sender, e) => Console.WriteLine($"Event from obj #{sender.GetHashCode()}");
Console.WriteLine($"Subscribed to event of obj #{image.GetHashCode()}");

image.RaiseEvent();
image.RaiseEvent();

var clone = image.DeepClone();
Console.WriteLine($"obj #{image.GetHashCode()} cloned to obj #{clone.GetHashCode()}");

clone.RaiseEvent();
image.RaiseEvent();

Ergebnis:

Created obj #46104728
Subscribed to event of obj #46104728
Event from obj #46104728
Event from obj #46104728
obj #46104728 cloned to obj #12289376
Event from obj #12289376
Event from obj #46104728

Beachten Sie, dass das Feld für die Ereignisunterstützung ebenfalls kopiert wird und der Client auch das Ereignis des Klons abonniert hat.

1
Ted Mucuzany

Dadurch werden alle lesbaren und beschreibbaren Eigenschaften eines Objekts in ein anderes kopiert.

 public class PropertyCopy<TSource, TTarget> 
                        where TSource: class, new()
                        where TTarget: class, new()
        {
            public static TTarget Copy(TSource src, TTarget trg, params string[] properties)
            {
                if (src==null) return trg;
                if (trg == null) trg = new TTarget();
                var fulllist = src.GetType().GetProperties().Where(c => c.CanWrite && c.CanRead).ToList();
                if (properties != null && properties.Count() > 0)
                    fulllist = fulllist.Where(c => properties.Contains(c.Name)).ToList();
                if (fulllist == null || fulllist.Count() == 0) return trg;

                fulllist.ForEach(c =>
                    {
                        c.SetValue(trg, c.GetValue(src));
                    });

                return trg;
            }
        }

und so benutzt man es:

 var cloned = Utils.PropertyCopy<TKTicket, TKTicket>.Copy(_tmp, dbsave,
                                                            "Creation",
                                                            "Description",
                                                            "IdTicketStatus",
                                                            "IdUserCreated",
                                                            "IdUserInCharge",
                                                            "IdUserRequested",
                                                            "IsUniqueTicketGenerated",
                                                            "LastEdit",
                                                            "Subject",
                                                            "UniqeTicketRequestId",
                                                            "Visibility");

oder um alles zu kopieren:

var cloned = Utils.PropertyCopy<TKTicket, TKTicket>.Copy(_tmp, dbsave);
1
Ylli Prifti

Haftungsausschluss: Ich bin der Autor des genannten Pakets.

Ich war überrascht, wie die Top-Antworten auf diese Frage im Jahr 2019 immer noch Serialisierung oder Reflektion verwenden.

Die Serialisierung ist einschränkend (erfordert Attribute, bestimmte Konstruktoren usw.) und sehr langsam

BinaryFormatter erfordert das Serializable -Attribut, JsonConverter erfordert einen parameterlosen Konstruktor oder parameterlose Attribute, behandelt keine schreibgeschützten Felder oder Schnittstellen sehr gut und beide sind 10-30x langsamer als nötig.

Ausdruck Bäume

Sie können stattdessen Expression Trees oder Reflection.Emit verwenden, um Kloncode nur einmal zu generieren. Verwenden Sie dann diesen kompilierten Code anstelle von langsamer Reflektion oder Serialisierung.

Nachdem ich selbst auf das Problem gestoßen war und keine zufriedenstellende Lösung gefunden hatte, entschloss ich mich, ein Paket zu erstellen, das genau das tut und funktioniert mit jedem Typ und ist fast so schnell wie kundenspezifisch geschriebener Code.

Sie finden das Projekt auf GitHub: https://github.com/marcelltoth/ObjectCloner

Verwendungszweck

Sie können es von NuGet installieren. Holen Sie sich entweder das Paket ObjectCloner und verwenden Sie es als:

var clone = ObjectCloner.DeepClone(original);

oder wenn es Ihnen nichts ausmacht, Ihren Objekttyp mit Erweiterungen zu verschmutzen, holen Sie sich auch ObjectCloner.Extensions und schreiben Sie:

var clone = original.DeepClone();

Performance

Ein einfacher Benchmark für das Klonen einer Klassenhierarchie ergab, dass die Leistung ca. 3-mal schneller als bei Verwendung von Reflection, ca. 12-mal schneller als bei der Newtonsoft.Json-Serialisierung und ca. 36-mal schneller als bei der stark empfohlenen BinaryFormatter.

0
Marcell Toth

System.Text.Json verwenden:

https://devblogs.Microsoft.com/dotnet/try-the-new-system-text-json-apis/

public static T DeepCopy<T>(this T source)
{
    return source == null ? default : JsonSerializer.Parse<T>(JsonSerializer.ToString(source));
}

Die neue API verwendet Span<T>. Das sollte schnell gehen, wäre schön einige Benchmarks zu machen.

Hinweis: ObjectCreationHandling.Replace ist nicht wie in Json.NET erforderlich, da es standardmäßig Auflistungswerte ersetzt. Sie sollten Json.NET jetzt vergessen, da alles durch die neue offizielle API ersetzt wird.

Ich bin nicht sicher, ob dies mit privaten Feldern funktioniert.

0
Konrad

Schnelles, einfaches und effektives Nuget-Paket zum Klonen

Nachdem ich alle Antworten gelesen hatte, war ich überrascht, dass niemand dieses hervorragende Paket erwähnte:

https://github.com/force-net/DeepCloner

Wir arbeiten ein wenig an der Readme-Datei, und hier sind die Gründe, warum wir uns für sie entschieden haben:

Haftungsausschluss - Anforderungen:

  • . NET 4.0 oder höher oder .NET Standard 1.3 (.NET Core)
  • Erfordert den Berechtigungssatz "Volle Vertrauenswürdigkeit" oder "Reflection" (MemberAccess)
  • Es kann tief oder flach kopieren
  • Beim tiefen Klonen bleiben alle Objektgraphen erhalten.
  • Verwendet die Codegenerierung zur Laufzeit, da das Klonen von Ergebnissen blitzschnell ist
  • Objekte kopiert durch interne Struktur, keine Methoden oder Ctors aufgerufen
  • Sie müssen keine Klassen markieren (wie Serializable-Attribut oder Schnittstellen implementieren)
  • Es muss kein Objekttyp für das Klonen angegeben werden. Das Objekt kann in eine Schnittstelle oder als abstraktes Objekt umgewandelt werden (z. B. können Sie ein Array von Ints als abstraktes Array oder IEnumerable klonen; sogar null kann fehlerfrei geklont werden).
  • Geklontes Objekt kann nicht feststellen, ob es sich um ein geklontes Objekt handelt (außer mit sehr spezifischen Methoden)

Die Verwendung ist so einfach:

  var deepClone = new { Id = 1, Name = "222" }.DeepClone();
  var shallowClone = new { Id = 1, Name = "222" }.ShallowClone();
0
alexlomba87