Ich möchte so etwas tun wie:
MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = myObj.Clone();
Nehmen Sie dann Änderungen am neuen Objekt vor, die sich nicht im ursprünglichen Objekt widerspiegeln.
Ich brauche diese Funktionalität nicht oft. Wenn es also nötig war, habe ich ein neues Objekt erstellt und dann jede Eigenschaft einzeln kopiert, aber ich habe immer das Gefühl, dass es eine bessere oder elegantere Art der Handhabung gibt die Situation.
Wie kann ich ein Objekt klonen oder tief kopieren, damit das geklonte Objekt geändert werden kann, ohne dass Änderungen im Originalobjekt berücksichtigt werden?
clone
Methode für die Klasse zu erstellen , und dann einen internen, privaten Konstruktor aufrufen zu lassen, der übergeben wirdthis
. Das Kopieren ist also turrible, aber das sorgfältige Kopieren (und der Artikel ist definitiv lesenswert) nicht. ; ^)Antworten:
Während die Standardpraxis darin besteht, die
ICloneable
Schnittstelle zu implementieren ( hier beschrieben , damit ich nicht wieder erbrechen kann), ist hier ein netter Deep-Clone-Objektkopierer, den ich vor einiger Zeit bei The Code Project gefunden und in unsere Inhalte aufgenommen habe.Wie an anderer Stelle erwähnt, müssen Ihre Objekte serialisierbar sein.
Die Idee ist, dass es Ihr Objekt serialisiert und es dann in ein neues Objekt deserialisiert. Der Vorteil ist, dass Sie sich nicht darum kümmern müssen, alles zu klonen, wenn ein Objekt zu komplex wird.
Und mit der Verwendung von Erweiterungsmethoden (auch aus der ursprünglich referenzierten Quelle):
Wenn Sie die neuen Erweiterungsmethoden von C # 3.0 bevorzugen , ändern Sie die Methode so, dass sie die folgende Signatur hat:
Jetzt wird der Methodenaufruf einfach
objectBeingCloned.Clone();
.BEARBEITEN (10. Januar 2015) Ich dachte, ich würde dies noch einmal überprüfen, um zu erwähnen, dass ich kürzlich damit begonnen habe, (Newtonsoft) Json zu verwenden. Es sollte leichter sein und den Overhead von [serialisierbaren] Tags vermeiden. ( NB @atconway hat in den Kommentaren darauf hingewiesen, dass private Mitglieder nicht mit der JSON-Methode geklont werden.)
quelle
typeof(T).IsSerializable
ist auch wahr, wenn der Typ mit dem[Serializable]
Attribut markiert wurde . DieISerializable
Schnittstelle muss nicht implementiert werden.Ich wollte einen Kloner für sehr einfache Objekte mit meist primitiven und Listen. Wenn Ihr Objekt standardmäßig JSON-serialisierbar ist, reicht diese Methode aus. Dies erfordert keine Änderung oder Implementierung von Schnittstellen in der geklonten Klasse, sondern nur einen JSON-Serializer wie JSON.NET.
Sie können diese Erweiterungsmethode auch verwenden
quelle
Newtonsoft.Json.JsonConvert
aber es ist der gleicheDer Grund, ICloneable nicht zu verwenden, liegt nicht darin, dass es keine generische Schnittstelle gibt. Der Grund, es nicht zu benutzen, ist, weil es vage ist . Es ist nicht klar, ob Sie eine flache oder eine tiefe Kopie erhalten. Das liegt beim Implementierer.
Ja,
MemberwiseClone
macht eine flache Kopie, aber das Gegenteil vonMemberwiseClone
ist nichtClone
; es wäre vielleicht dasDeepClone
, was es nicht gibt. Wenn Sie ein Objekt über seine ICloneable-Schnittstelle verwenden, können Sie nicht wissen, welche Art des Klonens das zugrunde liegende Objekt ausführt. (Und XML-Kommentare machen es nicht klar, da Sie eher die Schnittstellenkommentare als die Kommentare zur Klonmethode des Objekts erhalten.)Normalerweise mache ich einfach eine
Copy
Methode, die genau das macht, was ich will.quelle
Nachdem ich viel über viele der hier verlinkten Optionen und mögliche Lösungen für dieses Problem gelesen habe, glaube ich, dass alle Optionen unter Ian Ps Link ziemlich gut zusammengefasst sind (alle anderen Optionen sind Variationen davon) und die beste Lösung von bereitgestellt wird Pedro77 's Link zu den Fragenkommentaren.
Also kopiere ich einfach relevante Teile dieser 2 Referenzen hier. Auf diese Weise können wir haben:
Das Beste zum Klonen von Objekten in Cis!
In erster Linie sind dies alle unsere Optionen:
Der Artikel Fast Deep Copy nach Expressionsbäumen enthält auch einen Leistungsvergleich zum Klonen nach Serialisierungs-, Reflexions- und Expressionsbäumen.
Warum ich ICloneable wähle (dh manuell)
Herr Venkat Subramaniam (redundanter Link hier) erklärt ausführlich, warum .
Alle seine Artikel drehen sich um ein Beispiel, das in den meisten Fällen mit drei Objekten anwendbar sein soll: Person , Gehirn und Stadt . Wir wollen eine Person klonen, die ihr eigenes Gehirn hat, aber dieselbe Stadt. Sie können sich entweder alle Probleme vorstellen, die eine der anderen oben genannten Methoden mit sich bringen kann, oder den Artikel lesen.
Dies ist meine leicht modifizierte Version seiner Schlussfolgerung:
Hoffentlich kann diese Implementierung die Dinge klar machen:
Betrachten Sie nun eine Klasse, die von Person abgeleitet ist.
Sie können versuchen, den folgenden Code auszuführen:
Die erzeugte Ausgabe ist:
Beachten Sie, dass der hier implementierte Klon, wenn wir die Anzahl der Objekte zählen, die Anzahl der Objekte korrekt zählt.
quelle
ICloneable
für öffentliche Mitglieder zu verwenden. "Da Aufrufer von Clone nicht von der Methode abhängig sein können, die einen vorhersehbaren Klonvorgang ausführt, empfehlen wir, ICloneable nicht in öffentlichen APIs zu implementieren." msdn.microsoft.com/en-us/library/… Basierend auf der Erklärung von Venkat Subramaniam in Ihrem verlinkten Artikel halte ich es jedoch für sinnvoll, in dieser Situation zu verwenden, solange die Schöpfer der ICloneable-Objekte eine Tiefe haben Verständnis, welche Eigenschaften tief gegen flache Kopien sein sollten (dh tiefe Kopie Gehirn, flache Kopie Stadt)Ich ziehe einen Kopierkonstruktor einem Klon vor. Die Absicht ist klarer.
quelle
Einfache Erweiterungsmethode zum Kopieren aller öffentlichen Eigenschaften. Funktioniert für alle Objekte und erfordert keine Klasse
[Serializable]
. Kann für andere Zugriffsebenen erweitert werden.quelle
Ich habe gerade ein
CloneExtensions
Bibliotheksprojekt erstellt . Es führt ein schnelles, tiefes Klonen mit einfachen Zuweisungsoperationen durch, die durch die Kompilierung des Expression Tree-Laufzeitcodes generiert werden.Wie benutzt man es?
Anstatt eigene Methoden
Clone
oderCopy
Methoden mit einem Ton von Zuweisungen zwischen Feldern und Eigenschaften zu schreiben , muss das Programm dies mithilfe des Ausdrucksbaums selbst tun.GetClone<T>()
Mit der als Erweiterungsmethode gekennzeichneten Methode können Sie sie einfach auf Ihrer Instanz aufrufen:Sie können wählen , was kopiert werden soll ,
source
umnewInstance
mitCloningFlags
Enum:Was kann geklont werden?
Folgende Klassen- / Strukturmitglieder werden intern geklont:
Wie schnell ist es
Die Lösung ist schneller als die Reflexion, da die Mitgliederinformationen nur einmal gesammelt werden müssen, bevor
GetClone<T>
sie für einen bestimmten Typ zum ersten Mal verwendet werdenT
.Es ist auch schneller als eine auf Serialisierung basierende Lösung, wenn Sie mehr als mehrere Instanzen desselben Typs klonen
T
.und mehr...
Weitere Informationen zu generierten Ausdrücken finden Sie in der Dokumentation .
Beispiel für eine Debug-Liste für Ausdrucksausdrücke für
List<int>
:}}
Was hat die gleiche Bedeutung wie der folgende C # -Code:
Ist es nicht so, wie Sie Ihre eigene
Clone
Methode schreiben würdenList<int>
?quelle
Nun, ich hatte Probleme mit ICloneable in Silverlight, aber mir gefiel die Idee der Seralisierung. Ich kann XML seralisieren, also habe ich Folgendes getan:
quelle
Wenn Sie bereits eine Drittanbieteranwendung wie ValueInjecter oder Automapper verwenden , können Sie Folgendes tun:
Mit dieser Methode müssen Sie weder Ihre Objekte implementieren
ISerializable
nochICloneable
bearbeiten. Dies ist beim MVC / MVVM-Muster üblich, daher wurden einfache Tools wie dieses erstellt.Weitere Informationen finden Sie im Beispiel zum Deep Cloning von ValueInjecter auf GitHub .
quelle
Am besten implementieren Sie eine Erweiterungsmethode wie
und dann überall in der Lösung von verwenden
Wir können die folgenden drei Implementierungen haben:
Alle verknüpften Methoden funktionieren gut und wurden gründlich getestet.
quelle
Die kurze Antwort lautet: Sie erben von der ICloneable-Schnittstelle und implementieren dann die .clone-Funktion. Der Klon sollte eine Mitgliedskopie erstellen und eine Tiefenkopie für jedes Mitglied durchführen, das dies erfordert, und dann das resultierende Objekt zurückgeben. Dies ist eine rekursive Operation (es erfordert, dass alle Mitglieder der Klasse, die Sie klonen möchten, entweder Werttypen sind oder ICloneable implementieren und dass ihre Mitglieder entweder Werttypen sind oder ICloneable implementieren usw.).
Weitere Informationen zum Klonen mit ICloneable finden Sie in diesem Artikel .
Die lange Antwort lautet "es kommt darauf an". Wie von anderen erwähnt, wird ICloneable von Generika nicht unterstützt, erfordert spezielle Überlegungen für zirkuläre Klassenreferenzen und wird von einigen tatsächlich als "Fehler" angesehen. in .NET Framework angesehen. Die Serialisierungsmethode hängt davon ab, ob Ihre Objekte serialisierbar sind, was möglicherweise nicht der Fall ist und Sie möglicherweise keine Kontrolle darüber haben. Es gibt immer noch viele Debatten in der Community, über die die "beste" Praxis gesprochen wird. In der Realität ist keine der Lösungen die Einheitsgröße für alle Situationen, für die ICloneable ursprünglich interpretiert wurde.
In diesem Developer's Corner-Artikel finden Sie einige weitere Optionen (Dank an Ian).
quelle
Prost.
quelle
BEARBEITEN: Projekt wird eingestellt
Wenn Sie echtes Klonen auf unbekannte Typen wünschen, können Sie sich fastclone ansehen .
Das ausdrucksbasierte Klonen funktioniert etwa zehnmal schneller als die binäre Serialisierung und behält die vollständige Integrität des Objektgraphen bei.
Das heißt: Wenn Sie in Ihrer Hierarchie mehrmals auf dasselbe Objekt verweisen, wird auf den Klon auch eine einzelne Instanz verwiesen.
Es sind keine Schnittstellen, Attribute oder andere Änderungen an den zu klonenden Objekten erforderlich.
quelle
Halten Sie die Dinge einfach und verwenden Sie AutoMapper wie bereits erwähnt. Es ist eine einfache kleine Bibliothek, um ein Objekt einem anderen zuzuordnen. Um ein Objekt mit demselben Typ auf ein anderes zu kopieren, benötigen Sie lediglich drei Codezeilen:
Das Zielobjekt ist jetzt eine Kopie des Quellobjekts. Nicht einfach genug? Erstellen Sie eine Erweiterungsmethode, die Sie überall in Ihrer Lösung verwenden können:
Die Erweiterungsmethode kann wie folgt verwendet werden:
quelle
Ich habe mir dies ausgedacht, um ein .NET- Manko zu überwinden , bei dem List <T> manuell tief kopiert werden muss.
Ich benutze das:
Und an einem anderen Ort:
Ich habe versucht, einen Oneliner zu finden, der dies tut, aber es ist nicht möglich, da Yield nicht in anonymen Methodenblöcken funktioniert.
Besser noch, verwenden Sie den generischen List <T> Cloner:
quelle
F. Warum sollte ich diese Antwort wählen?
Mit anderen Worten, wählen Sie eine andere Antwort, es sei denn, Sie haben einen Leistungsengpass, der behoben werden muss, und Sie können dies mit einem Profiler beweisen .
10x schneller als andere Methoden
Die folgende Methode zum Ausführen eines tiefen Klons ist:
Und die Methode ...
Für ultimative Geschwindigkeit können Sie Nested MemberwiseClone verwenden, um eine tiefe Kopie zu erstellen . Es ist fast genauso schnell wie das Kopieren einer Wertestruktur und viel schneller als (a) Reflexion oder (b) Serialisierung (wie in anderen Antworten auf dieser Seite beschrieben).
Beachten Sie, dass Sie , wenn Sie Nested MemberwiseClone für eine Deep Copy verwenden , manuell eine ShallowCopy für jede verschachtelte Ebene in der Klasse und eine DeepCopy implementieren müssen, die alle genannten ShallowCopy-Methoden aufruft, um einen vollständigen Klon zu erstellen. Das ist ganz einfach: Insgesamt nur wenige Zeilen, siehe Demo-Code unten.
Hier ist die Ausgabe des Codes, die den relativen Leistungsunterschied für 100.000 Klone zeigt:
Die Verwendung von Nested MemberwiseClone für eine Klasse ist fast so schnell wie das Kopieren einer Struktur, und das Kopieren einer Struktur kommt der theoretischen Höchstgeschwindigkeit, zu der .NET fähig ist, verdammt nahe.
Um zu verstehen, wie mit MemberwiseCopy eine tiefe Kopie erstellt wird, finden Sie hier das Demo-Projekt, mit dem die oben genannten Zeiten generiert wurden:
Rufen Sie dann die Demo von main auf:
Beachten Sie erneut, dass Sie , wenn Sie Nested MemberwiseClone für eine tiefe Kopie verwenden , manuell eine ShallowCopy für jede verschachtelte Ebene in der Klasse und eine DeepCopy implementieren müssen, die alle genannten ShallowCopy-Methoden aufruft, um einen vollständigen Klon zu erstellen. Das ist ganz einfach: Insgesamt nur wenige Zeilen, siehe Demo-Code oben.
Werttypen vs. Referenztypen
Beachten Sie, dass es beim Klonen eines Objekts einen großen Unterschied zwischen einer " Struktur " und einer " Klasse " gibt:
Siehe Unterschiede zwischen Werttypen und Referenzen - Typen .
Prüfsummen zur Unterstützung des Debuggens
Wirklich nützlich, um viele Threads von vielen anderen Threads zu entkoppeln
Ein ausgezeichneter Anwendungsfall für diesen Code ist das Einspeisen von Klonen einer verschachtelten Klasse oder Struktur in eine Warteschlange, um das Producer / Consumer-Muster zu implementieren.
ConcurrentQueue
.Dies funktioniert in der Praxis sehr gut und ermöglicht es uns, viele Threads (die Produzenten) von einem oder mehreren Threads (den Verbrauchern) zu entkoppeln.
Und diese Methode ist auch unglaublich schnell: Wenn wir verschachtelte Strukturen verwenden, ist sie 35-mal schneller als das Serialisieren / Deserialisieren verschachtelter Klassen und ermöglicht es uns, alle auf dem Computer verfügbaren Threads zu nutzen.
Aktualisieren
Anscheinend ist ExpressMapper genauso schnell, wenn nicht sogar schneller als die Handcodierung wie oben. Ich muss vielleicht sehen, wie sie sich mit einem Profiler vergleichen.
quelle
Im Allgemeinen implementieren Sie die ICloneable-Schnittstelle und implementieren Clone selbst. C # -Objekte verfügen über eine integrierte MemberwiseClone-Methode, die eine flache Kopie ausführt, die Ihnen bei allen Grundelementen helfen kann.
Für eine tiefe Kopie kann es auf keinen Fall wissen, wie es automatisch gemacht wird.
quelle
Ich habe gesehen, dass es auch durch Reflexion umgesetzt wurde. Grundsätzlich gab es eine Methode, mit der die Elemente eines Objekts durchlaufen und entsprechend in das neue Objekt kopiert werden konnten. Als es Referenztypen oder Sammlungen erreichte, hat es sich meiner Meinung nach rekursiv aufgerufen. Reflexion ist teuer, aber es hat ziemlich gut funktioniert.
quelle
Hier ist eine Deep Copy-Implementierung:
quelle
Da ich in verschiedenen Projekten keinen Kloner finden konnte, der alle meine Anforderungen erfüllt, habe ich einen tiefen Kloner erstellt, der konfiguriert und an verschiedene Codestrukturen angepasst werden kann, anstatt meinen Code an die Anforderungen des Kloners anzupassen. Dies wird erreicht, indem dem zu klonenden Code Anmerkungen hinzugefügt werden, oder Sie lassen den Code einfach so, wie er das Standardverhalten haben soll. Es verwendet Reflection, Typ-Caches und basiert auf schnellerflect . Der Klonprozess ist für eine große Datenmenge und eine hohe Objekthierarchie sehr schnell (im Vergleich zu anderen auf Reflexion / Serialisierung basierenden Algorithmen).
https://github.com/kalisohn/CloneBehave
Auch als Nuget-Paket erhältlich: https://www.nuget.org/packages/Clone.Behave/1.0.0
Beispiel: Der folgende Code führt die DeepClone-Adresse aus, führt jedoch nur eine flache Kopie des Felds _currentJob aus.
quelle
Code Generator
Wir haben viele Ideen von der Serialisierung über die manuelle Implementierung bis zur Reflexion gesehen, und ich möchte einen völlig anderen Ansatz mit dem CGbR-Codegenerator vorschlagen . Die Generate Clone-Methode ist speicher- und CPU-effizient und daher 300x schneller als der Standard-DataContractSerializer.
Alles was Sie brauchen ist eine teilweise Klassendefinition mit
ICloneable
und der Generator erledigt den Rest:Hinweis: Die neueste Version enthält mehr Nullprüfungen, aber ich habe sie zum besseren Verständnis weggelassen.
quelle
Ich mag solche Copyconstructors:
Wenn Sie weitere Dinge kopieren möchten, fügen Sie diese hinzu
quelle
Diese Methode löste das Problem für mich:
Verwenden Sie es so:
MyObj a = DeepCopy(b);
quelle
Hier eine schnelle und einfache Lösung, die für mich funktioniert hat, ohne auf Serialisierung / Deserialisierung zu setzen.
EDIT : erfordert
So habe ich es benutzt
quelle
Folge diesen Schritten:
ISelf<T>
mit einer schreibgeschütztenSelf
Eigenschaft, die zurückgibtT
, undICloneable<out T>
die vonISelf<T>
einer Methode abgeleitet ist und diese enthältT Clone()
.CloneBase
Typ, der einprotected virtual generic VirtualClone
CastingMemberwiseClone
für den übergebenen Typ implementiert .VirtualClone
werden, indem die Basis-Klonmethode aufgerufen wird und dann alles getan wird, um die Aspekte des abgeleiteten Typs, die die übergeordnete VirtualClone-Methode noch nicht verarbeitet hat, ordnungsgemäß zu klonen.Für eine maximale Vielseitigkeit der Vererbung sollten Klassen, die öffentliche Klonfunktionen
sealed
bereitstellen, eine Basisklasse sein, die bis auf das Fehlen des Klonens ansonsten identisch ist. Anstatt Variablen vom expliziten klonbaren Typ zu übergeben, nehmen Sie einen Parameter vom TypICloneable<theNonCloneableType>
. Dies ermöglicht eine Routine, die erwartet, dass eine klonbare Ableitung vonFoo
mit einer klonbaren Ableitung von arbeitetDerivedFoo
, ermöglicht aber auch die Erstellung nicht klonbarer Ableitungen vonFoo
.quelle
Ich denke, Sie können das versuchen.
quelle
Ich habe eine Version der akzeptierten Antwort erstellt, die sowohl mit '[Serializable]' als auch mit '[DataContract]' funktioniert. Es ist eine Weile her, seit ich es geschrieben habe, aber wenn ich mich richtig erinnere, brauchte [DataContract] einen anderen Serializer.
Benötigt System, System.IO, System.Runtime.Serialization, System.Runtime.Serialization.Formatters.Binary, System.Xml ;
quelle
Ok, es gibt ein offensichtliches Beispiel für die Reflexion in diesem Beitrag, ABER die Reflexion ist normalerweise langsam, bis Sie anfangen, sie richtig zwischenzuspeichern.
Wenn Sie es richtig zwischenspeichern, wird das 1000000-Objekt um 4,6 Sekunden (gemessen von Watcher) tief geklont.
als Sie zwischengespeicherte Eigenschaften nehmen oder neue zum Wörterbuch hinzufügen und sie einfach verwenden
Vollständiger Code Check in meinem Beitrag in einer anderen Antwort
https://stackoverflow.com/a/34365709/4711853
quelle
prop.GetValue(...)
ist immer noch eine Reflexion und kann nicht zwischengespeichert werden. In einem Ausdrucksbaum ist es allerdings so schnell kompiliertDa fast alle Antworten auf diese Frage unbefriedigend waren oder in meiner Situation eindeutig nicht funktionieren, habe ich AnyClone verfasst, das vollständig mit Reflexion implementiert und alle Anforderungen hier gelöst hat. Ich konnte die Serialisierung in einem komplizierten Szenario mit komplexer Struktur nicht zum Laufen bringen und
IClonable
ist weniger als ideal - tatsächlich sollte es nicht einmal notwendig sein.Standard-Ignorierattribute werden unterstützt mit
[IgnoreDataMember]
,[NonSerialized]
. Unterstützt komplexe Sammlungen, Eigenschaften ohne Setter, schreibgeschützte Felder usw.Ich hoffe, es hilft jemand anderem da draußen, der auf die gleichen Probleme gestoßen ist wie ich.
quelle
Haftungsausschluss: Ich bin der Autor des genannten Pakets.
Ich war überrascht, wie die Top-Antworten auf diese Frage im Jahr 2019 immer noch Serialisierung oder Reflexion verwenden.
Die Serialisierung ist einschränkend (erfordert Attribute, bestimmte Konstruktoren usw.) und ist sehr langsam
BinaryFormatter
erfordert dasSerializable
Attribut,JsonConverter
erfordert einen parameterlosen Konstruktor oder Attribute, behandelt keine schreibgeschützten Felder oder Schnittstellen sehr gut und beide sind 10-30x langsamer als nötig.Ausdrucksbäume
Sie können stattdessen Ausdrucksbäume oder Reflection.Emit verwenden , um Kloncode nur einmal zu generieren, und dann diesen kompilierten Code anstelle von langsamer Reflektion oder Serialisierung verwenden.
Nachdem ich selbst auf das Problem gestoßen war und keine zufriedenstellende Lösung gefunden hatte, entschied ich mich, ein Paket zu erstellen, das genau das tut und mit jedem Typ funktioniert und fast so schnell ist wie benutzerdefinierter geschriebener Code .
Sie finden das Projekt auf GitHub: https://github.com/marcelltoth/ObjectCloner
Verwendungszweck
Sie können es von NuGet installieren. Holen Sie sich das
ObjectCloner
Paket und verwenden Sie es als:oder wenn es Ihnen nichts ausmacht, Ihren Objekttyp mit Erweiterungen zu verschmutzen, erhalten Sie
ObjectCloner.Extensions
auch und schreiben Sie:Performance
Ein einfacher Benchmark zum Klonen einer Klassenhierarchie zeigte eine Leistung ~ 3x schneller als bei Verwendung von Reflection, ~ 12x schneller als bei der Newtonsoft.Json-Serialisierung und ~ 36x schneller als die empfohlene
BinaryFormatter
.quelle