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

7voto

GorvGoyl Punkte 32029

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 so: MyObj a = DeepCopy(b);

7voto

Daniele D. Punkte 2524

Hier eine schnelle und einfache Lösung, die bei mir funktioniert hat, ohne auf Serialisierung/Deserialisierung zurückzugreifen.

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

6voto

Erçin Dedeoğlu Punkte 4262

Kürzester Weg, aber Abhängigkeit erforderlich:

using Newtonsoft.Json;
    public static T Clone<T>(T source) =>
        JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source));

6voto

supercat Punkte 72939

Folgen Sie diesen Schritten:

  • Definieren Sie eine ISelf<T> mit einem Nur-Lese Self Eigenschaft, die Folgendes zurückgibt T y ICloneable<out T> die sich ableitet von ISelf<T> und umfasst eine Methode T Clone() .
  • Dann definieren Sie eine CloneBase Typ, der eine protected virtual generic VirtualClone Gießen MemberwiseClone auf den übergebenen Typ.
  • Jeder abgeleitete Typ sollte Folgendes implementieren VirtualClone durch Aufrufen der Klon-Methode des Basistyps und anschließendes Erledigen der Aufgaben, die erforderlich sind, um die Aspekte des abgeleiteten Typs, die die übergeordnete VirtualClone-Methode noch nicht behandelt hat, ordnungsgemäß zu klonen.

Für maximale Vielseitigkeit bei der Vererbung sollten Klassen mit öffentlicher Klon-Funktionalität sealed , sondern leiten sich von einer Basisklasse ab, die ansonsten bis auf das fehlende Klonen identisch ist. Anstatt Variablen vom explizit klonierbaren Typ zu übergeben, nehmen Sie einen Parameter vom Typ ICloneable<theNonCloneableType> . Dies ermöglicht einer Routine, die eine klonbare Ableitung von Foo um mit einem klonbaren Derivat von DerivedFoo sondern erlauben auch die Erstellung von nicht klonierbaren Derivaten von Foo .

6voto

Izzy Punkte 1605

C# 9.0 führt die with Schlüsselwort, das ein record (Danke Mark Nading). Dies sollte sehr einfaches Klonen von Objekten (und Mutation, falls erforderlich) mit sehr wenig Boilerplate ermöglichen, aber nur mit einer record .

Es scheint nicht möglich zu sein, eine Klasse (nach Wert) zu klonen, indem man sie in eine generische record ;

using System;

public class Program
{
    public class Example
    {
        public string A { get; set; }
    }

    public record ClonerRecord<T>(T a)
    {
    }

    public static void Main()
    {
        var foo = new Example {A = "Hello World"};
        var bar = (new ClonerRecord<Example>(foo) with {}).a;
        foo.A = "Goodbye World :(";
        Console.WriteLine(bar.A);
    }
}

Dies schreibt "Goodbye World :(" - die Zeichenkette wurde per Referenz kopiert (unerwünscht). https://dotnetfiddle.net/w3IJgG

(Unglaublicherweise funktioniert das oben Gesagte korrekt mit einer struct ! https://dotnetfiddle.net/469NJv )

Aber das Klonen eines record scheint wie eingerückt zu funktionieren, Klonen nach Wert.

using System;

public class Program
{
    public record Example
    {
        public string A { get; set; }
    }

    public static void Main()
    {
        var foo = new Example {A = "Hello World"};
        var bar = foo with {};
        foo.A = "Goodbye World :(";
        Console.WriteLine(bar.A);
    }
}

Dies ergibt "Hello World", die Zeichenkette wurde nach Wert kopiert! https://dotnetfiddle.net/MCHGEL

Weitere Informationen finden Sie in diesem Blogbeitrag:

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/with-expression

1 Stimmen

Nach dem, was ich gelesen habe, ist dies nur für den neuen "Record"-Typ. Einer von uns sollte das wirklich einfach in .net fiddle ausprobieren :P

0 Stimmen

@MarkNadig Das war mir gar nicht aufgefallen! Es sieht so aus, als ob die Verwendung einer record zum Klonen einer class Funktioniert nicht. dotnetfiddle.net/w3IJgG Aber Klonen für eine Wohnung record scheint nach Wert zu kopieren! dotnetfiddle.net/MCHGEL

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