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

10voto

dougajmcdonald Punkte 17991

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

2 Stimmen

Dies sieht wie ein mitgliederweises Klonen aus, da es die Eigenschaften des Referenztyps nicht kennt

1 Stimmen

Wenn Sie eine blitzschnelle Leistung wünschen, sollten Sie sich nicht für diese Implementierung entscheiden: Sie verwendet Reflection, also wird sie nicht so schnell sein. Umgekehrt ist "verfrühte Optmierung das größte Übel", also ignorieren Sie die Leistungsseite, bis Sie einen Profiler ausgeführt haben.

1 Stimmen

CreateInstanceOfType ist nicht definiert?

10voto

xr280xr Punkte 11651

Ich habe gesehen, dass es auch durch Reflexion umgesetzt wird. Im Grunde gab es eine Methode, die durch die Mitglieder eines Objekts iteriert und sie entsprechend in das neue Objekt kopiert. Wenn sie Referenztypen oder Sammlungen erreicht, glaube ich, dass sie einen rekursiven Aufruf auf sich selbst macht. Reflection ist teuer, aber es hat ziemlich gut funktioniert.

8voto

kalisohn Punkte 301

Da ich keinen Kloner finden konnte, der alle meine Anforderungen in verschiedenen Projekten erfüllt, habe ich einen Deep Cloner erstellt, der konfiguriert und an verschiedene Codestrukturen angepasst werden kann, anstatt meinen Code an die Anforderungen des Kloners anzupassen. Erreicht wird dies durch das Hinzufügen von Anmerkungen zu dem Code, der geklont werden soll, oder man lässt den Code einfach so, wie er ist, um das Standardverhalten zu erhalten. Es verwendet Reflexion, Typ-Caches und basiert auf fasterflect . Der Klonprozess ist bei großen Datenmengen und einer hohen Objekthierarchie sehr schnell (im Vergleich zu anderen auf Reflexion/Serialisierung basierenden Algorithmen).

https://github.com/kalisohn/CloneBehave

Auch als Nuget-Paket verfügbar: https://www.nuget.org/packages/Clone.Behave/1.0.0

Zum Beispiel: Mit dem folgenden Code wird die Adresse tief geklont, aber nur eine oberflächliche Kopie des Feldes _currentJob durchgeführt.

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

8voto

Toxantron Punkte 2158

Code-Generator

Wir haben viele Ideen gesehen, von Serialisierung über manuelle Implementierung bis hin zu Reflection, und ich möchte einen völlig anderen Ansatz vorschlagen, der die CGbR-Code-Generator . Die generierte Klon-Methode ist speicher- und CPU-effizient und daher 300x 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;
    }
}

Anmerkung: In der neuesten Version gibt es mehr Nullprüfungen, aber ich habe sie zum besseren Verständnis weggelassen.

8voto

LuckyLikey Punkte 3194

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 noch mehr Dinge zu kopieren haben, fügen Sie sie hinzu

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