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

1voto

KeyNone Punkte 8037

Wenn Sie Marc Gravells protobuf-net als Serialisierer verwenden, muss die akzeptierte Antwort leicht geändert werden, da das zu kopierende Objekt nicht mit [Serializable] und ist daher nicht serialisierbar, und die Clone-Methode löst eine Ausnahme aus.
Ich habe es so geändert, 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);
    }
}

Dies prüft das Vorhandensein einer [ProtoContract] Attribut und verwendet protobufs eigenen Formatierer zur Serialisierung des Objekts.

0voto

Für den Klonvorgang kann das Objekt zunächst in ein Byte-Array konvertiert und dann wieder in ein Objekt umgewandelt werden.

public static class Extentions
{
    public static T Clone<T>(this T obj)
    {
        byte[] buffer = BinarySerialize(obj);
        return (T)BinaryDeserialize(buffer);
    }

    public static byte[] BinarySerialize(object obj)
    {
        using (var stream = new MemoryStream())
        {
            var formatter = new BinaryFormatter(); 
            formatter.Serialize(stream, obj); 
            return stream.ToArray();
        }
    }

    public static object BinaryDeserialize(byte[] buffer)
    {
        using (var stream = new MemoryStream(buffer))
        {
           var formatter = new BinaryFormatter(); 
           return formatter.Deserialize(stream);
        }
    }
}

Das Objekt muss für den Serialisierungsprozess serialisiert werden.

[Serializable]
public class MyObject
{
    public string Name  { get; set; }
}

Uso:

MyObject myObj  = GetMyObj();
MyObject newObj = myObj.Clone();

0voto

ʞᴉɯ Punkte 4996

Ich habe dieses Paket gefunden, das schneller zu sein scheint als die DeepCloner und ohne Abhängigkeiten, verglichen mit ihm.

https://github.com/AlenToma/FastDeepCloner

-2voto

gaa Punkte 1102

Ich weiß, dass diese Frage und Antwort sitzt hier für eine Weile und das folgende ist nicht wirklich eine Antwort, sondern eher eine Beobachtung, auf die ich vor kurzem gestoßen bin, als ich überprüfte, ob tatsächlich keine Privaten geklont werden (ich wäre nicht ich selbst, wenn ich es nicht getan hätte ;), als ich fröhlich @johnc kopierte aktualisierte Antwort .

Ich habe mich einfach Erweiterung Methode (die ziemlich viel copy-pasted Form vorgenannten Antwort ist):

public static class CloneThroughJsonExtension
{
    private static readonly JsonSerializerSettings DeserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };

    public static T CloneThroughJson<T>(this T source)
    {
        return ReferenceEquals(source, null) ? default(T) : JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source), DeserializeSettings);
    }
}

und ließ naiv Klasse wie diese (in der Tat gab es mehr von denen, aber sie sind nicht verwandt):

public class WhatTheHeck
{
    public string PrivateSet { get; private set; } // matches ctor param name

    public string GetOnly { get; } // matches ctor param name

    private readonly string _indirectField;
    public string Indirect => $"Inception of: {_indirectField} "; // matches ctor param name
    public string RealIndirectFieldVaule => _indirectField;

    public WhatTheHeck(string privateSet, string getOnly, string indirect)
    {
        PrivateSet = privateSet;
        GetOnly = getOnly;
        _indirectField = indirect;
    }
}

und Code wie diesen:

var clone = new WhatTheHeck("Private-Set-Prop cloned!", "Get-Only-Prop cloned!", "Indirect-Field clonned!").CloneThroughJson();
Console.WriteLine($"1. {clone.PrivateSet}");
Console.WriteLine($"2. {clone.GetOnly}");
Console.WriteLine($"3.1. {clone.Indirect}");
Console.WriteLine($"3.2. {clone.RealIndirectFieldVaule}");

führte:

1. Private-Set-Prop cloned!
2. Get-Only-Prop cloned!
3.1. Inception of: Inception of: Indirect-Field cloned!
3.2. Inception of: Indirect-Field cloned!

Ich war ganz wie: WHAT THE F... so schnappte ich mir Newtonsoft.Json Github Repo und begann zu graben. Was dabei herauskommt, ist, dass: während des Deserialisierens eines Typs, der zufällig nur einen Ctor hat und dessen Parameternamen übereinstimmen ( Groß- und Kleinschreibung wird nicht berücksichtigt ) öffentliche Eigenschaftsnamen, so werden diese als Parameter an ctor übergeben. Einige Hinweise können im Code gefunden werden aquí y aquí .

Unterm Strich

Ich weiß, dass dies eher nicht der Regelfall ist und der Beispielcode etwas missbräuchlich ist, aber hey! Es hat mich überrascht, als ich überprüft habe, ob ein Drache im Gebüsch darauf wartet, herauszuspringen und mich in den Hintern zu beißen ;)

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