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

3voto

Mauro Sampietro Punkte 2580

Ein Mapper führt eine Deep-Copy durch. Für jedes Element Ihres Objekts erstellt er ein neues Objekt und weist alle seine Werte zu. Er arbeitet rekursiv an jedem nicht-primitiven inneren Mitglied.

Ich empfehle Ihnen eines der schnellsten, derzeit aktiv entwickelten Programme. Ich empfehle UltraMapper https://github.com/maurosampietro/UltraMapper

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

0 Stimmen

Ein Link zu einer Lösung ist willkommen, aber bitte stellen Sie sicher, dass Ihre Antwort auch ohne diesen nützlich ist: Kontext um den Link hinzufügen damit Ihre Mitnutzer eine Vorstellung davon haben, worum es sich handelt und warum es dort ist, und zitieren Sie dann den wichtigsten Teil der Seite, auf die Sie verlinken, für den Fall, dass die Zielseite nicht verfügbar ist. Antworten, die kaum mehr als ein Link sind, können gelöscht werden.

3voto

vivek nuna Punkte 16546

Ich werde die folgende einfache Methode verwenden, um dies umzusetzen. Erstellen Sie einfach eine abstrakte Klasse und implementieren Sie eine Methode zur Serialisierung und Deserialisierung und Rückkehr.

public abstract class CloneablePrototype<T>
{
    public T DeepCopy()
    {
        string result = JsonConvert.SerializeObject(this);
        return JsonConvert.DeserializeObject<T>(result);
    }
}
public class YourClass : CloneablePrototype< YourClass>
…
…
…

Und sie verwenden es so, um tiefgehende Texte zu erstellen.

YourClass newObj = (YourClass)oldObj.DeepCopy();

Diese Lösung lässt sich auch leicht erweitern, wenn Sie die Methode der flachen Kopie ebenfalls implementieren möchten.

Implementieren Sie einfach eine neue Methode in der abstrakten Klasse.

public T ShallowCopy()
{
    return (T)this.MemberwiseClone();
}

3voto

Sameera R. Punkte 4076

C#-Erweiterung mit Unterstützung für "not ISerializable " Typen zu.

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

Verwendung

       var obj2 = obj1.DeepClone()

2voto

Cinorid Punkte 59

Ich habe einige Benchmarks zu den aktuellen Antworten durchgeführt und einige interessante Fakten gefunden.

Verwendung von BinarySerializer => https://stackoverflow.com/a/78612/6338072

XmlSerializer verwenden => https://stackoverflow.com/a/50150204/6338072

Verwendung von Activator.CreateInstance => https://stackoverflow.com/a/56691124/6338072

Dies sind die Ergebnisse

BenchmarkDotNet=v0.13.1, OS=Windows 10.0.18363.1734 (1909/November2019Update/19H2)

Intel Core i5-6200U CPU 2.30GHz (Skylake), 1 CPU, 4 logische und 2 physische Kerne [Host] : .NET Framework 4.8 (4.8.4400.0), X86 LegacyJIT DefaultJob : .NET Framework 4.8 (4.8.4400.0), X86 LegacyJIT

Methode

Mittlere

Fehler

StdDev

Gen 0

Zuteilung

BinarySerializer

220,69 us

4.374 us

9.963 us

49.8047

77 KB

XmlSerializer

182,72 us

3.619 us

9.405 us

21.9727

34 KB

Aktivator.CreateInstance

49,99 us

0,992 us

2.861 us

1.9531

3 KB

2voto

Ted Mucuzany Punkte 35

Beim Deep Cloning geht es um das Kopieren Staat . Für .net Staat 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 durchgeführt werden:

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 zufällige Werte für random Feld, sondern clone entspricht genau dem image

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 Backing-Feld des Ereignisses ebenfalls kopiert wird und der Client auch das Ereignis des Klons abonniert hat.

0 Stimmen

Das ist es wirklich. Es kann heikle Nebenwirkungen verursachen, daher sollte es mit Vorsicht verwendet werden.

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