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

Ylli Prifti Punkte 171

Dadurch werden alle lesbaren und schreibbaren 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 verwenden Sie es:

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

oder alles zu kopieren:

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

1voto

lindexi Punkte 3956

Ich habe eine neue Methode gefunden, die Emit heißt.

Wir können Emit verwenden, um die AWL zur Anwendung hinzuzufügen und sie auszuführen. Aber ich glaube nicht, dass es ein guter Weg, denn ich möchte dies zu perfektionieren, dass ich meine Antwort schreiben.

Der Emitter kann die offizielles Dokument y Leitfaden

Sie sollten einige 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 tief kopiert werden, aber er kann die Eigenschaft kopieren. Wenn Sie es machen wollen, um tiefe Kopie, die Sie es für die IL ändern können, ist zu schwer, dass ich es tun kann.

1voto

Ogglas Punkte 48648

Eine Ergänzung zu @Konrad und @craastad mit der Verwendung von eingebauten System.Text.Json para .NET >5

https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-how-to?pivots=dotnet-5-0

Methode:

public static T Clone<T>(T source)
{
    var serialized = JsonSerializer.Serialize(source);
    return JsonSerializer.Deserialize<T>(serialized);
}

Methode der Erweiterung:

public static class SystemExtension
{
    public static T Clone<T>(this T source)
    {
        var serialized = JsonSerializer.Serialize(source);
        return JsonSerializer.Deserialize<T>(serialized);
    }
}

0 Stimmen

Wenn Sie ablehnen, fügen Sie bitte einen Kommentar hinzu, warum, da es sonst schwierig ist, die Antworten zu verbessern.

1voto

Adel Tabareh Punkte 1172

Using System.Text.Json;

public static class CloneExtensions
{
    public static T Clone<T>(this T cloneable) where T : new()
    {
        var toJson = JsonSerializer.Serialize(cloneable);
        return JsonSerializer.Deserialize<T>(toJson);
    }
}

1voto

will_m Punkte 29

Wie wäre es, wenn Sie einfach innerhalb einer Methode umschreiben die im Grunde 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

0 Stimmen

Ich habe versucht, ein Objekt mit Eigenschaften mit einfachen Typen und Referenztypen umzuwandeln. Nur tat eine flache Kopie der Eigenschaft, die ein Verweistyp war.

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