Je veux faire quelque chose comme:
MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = myObj.Clone();
Et puis apportez des modifications au nouvel objet qui ne sont pas reflétées dans l'objet d'origine.
Je n'ai pas souvent besoin de cette fonctionnalité, donc quand cela a été nécessaire, j'ai eu recours à la création d'un nouvel objet puis à la copie de chaque propriété individuellement, mais cela me laisse toujours le sentiment qu'il existe une manière meilleure ou plus élégante de gérer la situation.
Comment puis-je cloner ou copier en profondeur un objet afin que l'objet cloné puisse être modifié sans qu'aucune modification ne soit reflétée dans l'objet d'origine?
clone
méthode sur la classe, puis de la faire appeler un constructeur interne et privé qui est passéthis
. Donc, la copie est turbulente [sic], mais la copie avec soin (et l'article mérite certainement d'être lu) ne l'est pas. ; ^)Réponses:
Bien que la pratique standard consiste à implémenter l'
ICloneable
interface (décrite ici , donc je ne régurgite pas), voici un joli copieur d'objets clone profond que j'ai trouvé sur The Code Project un certain temps et que incorporé dans nos trucs.Comme mentionné ailleurs, cela nécessite que vos objets soient sérialisables.
L'idée est qu'il sérialise votre objet puis le désérialise en un nouvel objet. L'avantage est que vous n'avez pas à vous soucier de tout cloner lorsqu'un objet devient trop complexe.
Et avec l'utilisation de méthodes d'extension (également à partir de la source référencée à l'origine):
Si vous préférez utiliser les nouvelles méthodes d'extension de C # 3.0, changez la méthode pour avoir la signature suivante:
Maintenant, l'appel de méthode devient simplement
objectBeingCloned.Clone();
.EDIT (10 janvier 2015) J'ai pensé revoir cela, pour mentionner que j'ai récemment commencé à utiliser (Newtonsoft) Json pour ce faire, il devrait être plus léger et éviter la surcharge des balises [Serializable]. ( NB @atconway a souligné dans les commentaires que les membres privés ne sont pas clonés en utilisant la méthode JSON)
la source
typeof(T).IsSerializable
est également vrai si le type a été marqué avec l'[Serializable]
attribut. Il n'a pas besoin d'implémenter l'ISerializable
interface.Je voulais un cloneur pour des objets très simples, principalement des primitives et des listes. Si votre objet est hors de la boîte JSON sérialisable, cette méthode fera l'affaire. Cela ne nécessite aucune modification ni implémentation d'interfaces sur la classe clonée, juste un sérialiseur JSON comme JSON.NET.
Vous pouvez également utiliser cette méthode d'extension
la source
Newtonsoft.Json.JsonConvert
mais c'est la même choseLa raison de ne pas utiliser ICloneable n'est pas parce qu'il n'a pas d'interface générique. La raison de ne pas l'utiliser est parce que c'est vague . Il n'est pas clair si vous obtenez une copie peu profonde ou profonde; c'est à l'implémenteur.
Oui,
MemberwiseClone
fait une copie superficielle, mais l'opposé deMemberwiseClone
ne l'est pasClone
; ce serait, peut-être,DeepClone
, qui n'existe pas. Lorsque vous utilisez un objet via son interface ICloneable, vous ne pouvez pas savoir quel type de clonage l'objet sous-jacent effectue. (Et les commentaires XML ne seront pas clairs, car vous obtiendrez les commentaires d'interface plutôt que ceux de la méthode Clone de l'objet.)Ce que je fais habituellement, c'est simplement de créer une
Copy
méthode qui fait exactement ce que je veux.la source
Après beaucoup de lecture sur de nombreuses options liées ici et sur les solutions possibles à ce problème, je pense que toutes les options sont assez bien résumées sur le lien de Ian P (toutes les autres options sont des variantes de celles-ci) et la meilleure solution est fournie par Lien de Pedro77 sur les commentaires de la question.
Je vais donc simplement copier les parties pertinentes de ces 2 références ici. De cette façon, nous pouvons avoir:
La meilleure chose à faire pour cloner des objets en C sharp!
Avant tout, ce sont toutes nos options:
L' article Fast Deep Copy par Expression Trees propose également une comparaison des performances du clonage par sérialisation, réflexion et expression.
Pourquoi je choisis ICloneable (c'est-à-dire manuellement)
M. Venkat Subramaniam (lien redondant ici) explique en détail pourquoi .
Tous ses articles tournent autour d'un exemple qui tente de s'appliquer à la plupart des cas, en utilisant 3 objets: Personne , Cerveau et Ville . Nous voulons cloner une personne, qui aura son propre cerveau mais la même ville. Vous pouvez soit visualiser tous les problèmes que l'une des autres méthodes ci-dessus peut apporter ou lire l'article.
Voici ma version légèrement modifiée de sa conclusion:
Espérons que cette mise en œuvre puisse clarifier les choses:
Envisagez maintenant d'avoir une classe dérivée de Person.
Vous pouvez essayer d'exécuter le code suivant:
La sortie produite sera:
Notez que si nous comptons le nombre d'objets, le clone tel qu'implémenté ici gardera un compte correct du nombre d'objets.
la source
ICloneable
pour les membres publics. "Parce que les appelants de Clone ne peuvent pas dépendre de la méthode effectuant une opération de clonage prévisible, nous recommandons qu'ICloneable ne soit pas implémenté dans les API publiques." msdn.microsoft.com/en-us/library/… Cependant, sur la base de l'explication donnée par Venkat Subramaniam dans votre article lié, je pense qu'il est logique d'utiliser dans cette situation tant que les créateurs des objets ICloneable ont une profonde comprendre quelles propriétés doivent être profondes par rapport aux copies superficielles (c.-à-d. copie profonde Brain, copie superficielle City)Je préfère un constructeur de copie à un clone. L'intention est plus claire.
la source
Méthode d'extension simple pour copier toutes les propriétés publiques. Fonctionne pour tous les objets et ne nécessite pas de classe
[Serializable]
. Peut être étendu pour un autre niveau d'accès.la source
Je viens de créer un projet de
CloneExtensions
bibliothèque . Il effectue un clonage rapide et profond à l'aide d'opérations d'affectation simples générées par la compilation de code d'exécution d'Expression Tree.Comment l'utiliser?
Au lieu d'écrire vos propres méthodes
Clone
ouCopy
avec un ton d'affectations entre les champs et les propriétés, faites-le faire par vous-même, en utilisant l'arbre d'expression.GetClone<T>()
La méthode marquée comme méthode d'extension vous permet de l'appeler simplement sur votre instance:Vous pouvez choisir ce qui doit être copié de
source
à l'newInstance
aide deCloningFlags
enum:Que peut-on cloner?
Les membres de classe / structure suivants sont clonés en interne:
Quelle est sa vitesse?
La solution est plus rapide que la réflexion, car les informations sur les membres ne doivent être collectées qu'une seule fois, avant d'
GetClone<T>
être utilisées pour la première fois pour un type donnéT
.C'est également plus rapide qu'une solution basée sur la sérialisation lorsque vous clonez plus de deux instances du même type
T
.et plus...
En savoir plus sur les expressions générées dans la documentation .
Exemple de liste de débogage d'expression pour
List<int>
:}
ce qui a la même signification que de suivre le code c #:
N'est-ce pas tout à fait comme ça que vous écririez votre propre
Clone
méthodeList<int>
?la source
Eh bien, j'avais des problèmes avec ICloneable dans Silverlight, mais j'ai aimé l'idée de la sérialisation, je peux séraliser XML, alors j'ai fait ceci:
la source
Si vous utilisez déjà une application tierce comme ValueInjecter ou Automapper , vous pouvez faire quelque chose comme ceci:
En utilisant cette méthode, vous n'avez pas à implémenter
ISerializable
ouICloneable
sur vos objets. Ceci est courant avec le modèle MVC / MVVM, donc des outils simples comme celui-ci ont été créés.voir l'exemple de clonage profond ValueInjecter sur GitHub .
la source
Le mieux est d'implémenter une méthode d'extension comme
puis l'utiliser n'importe où dans la solution en
Nous pouvons avoir les trois implémentations suivantes:
Toutes les méthodes liées fonctionnent bien et ont été testées en profondeur.
la source
La réponse courte est que vous héritez de l'interface ICloneable puis implémentez la fonction .clone. Le clone doit effectuer une copie par membre et effectuer une copie complète sur tout membre qui en a besoin, puis renvoyer l'objet résultant. Il s'agit d'une opération récursive (elle requiert que tous les membres de la classe que vous souhaitez cloner soient soit des types de valeur, soit implémentent ICloneable et que leurs membres soient soit des types de valeur, soit implémentent ICloneable, etc.).
Pour une explication plus détaillée sur le clonage à l'aide d'ICloneable, consultez cet article .
La réponse longue est "ça dépend". Comme mentionné par d'autres, ICloneable n'est pas pris en charge par les génériques, nécessite des considérations spéciales pour les références de classe circulaires et est en fait considéré par certains comme une "erreur" dans le .NET Framework. La méthode de sérialisation dépend de la sérialisation de vos objets, ce qu'ils peuvent ne pas être et que vous n'avez aucun contrôle sur. Il y a encore beaucoup de débats dans la communauté sur la meilleure pratique. En réalité, aucune des solutions ne correspond à la meilleure pratique pour toutes les situations comme ICloneable a été interprétée à l'origine.
Consultez cet article du coin du développeur pour quelques autres options (crédit à Ian).
la source
À votre santé.
la source
EDIT: le projet est interrompu
Si vous voulez un vrai clonage vers des types inconnus, vous pouvez jeter un œil à fastclone .
Le clonage basé sur l'expression fonctionne environ 10 fois plus rapidement que la sérialisation binaire et maintient l'intégrité complète du graphique d'objet.
Cela signifie: si vous faites référence plusieurs fois au même objet dans votre hiérarchie, le clone aura également une seule instance référencée.
Il n'y a pas besoin d'interfaces, d'attributs ou de toute autre modification des objets à cloner.
la source
Gardez les choses simples et utilisez AutoMapper comme d'autres l'ont mentionné, c'est une simple petite bibliothèque pour mapper un objet à un autre ... Pour copier un objet à un autre avec le même type, tout ce dont vous avez besoin est de trois lignes de code:
L'objet cible est maintenant une copie de l'objet source. Pas assez simple? Créez une méthode d'extension à utiliser partout dans votre solution:
La méthode d'extension peut être utilisée comme suit:
la source
Je suis venu avec cela pour surmonter un .NET lacune devant copier manuellement en profondeur la liste <T>.
J'utilise ceci:
Et à un autre endroit:
J'ai essayé de trouver un oneliner qui fait cela, mais ce n'est pas possible, car le rendement ne fonctionne pas à l'intérieur des blocs de méthodes anonymes.
Mieux encore, utilisez le cloneur générique List <T>:
la source
Q. Pourquoi devrais-je choisir cette réponse?
En d'autres termes, allez avec une autre réponse, sauf si vous avez un goulot d'étranglement des performances qui doit être corrigé, et vous pouvez le prouver avec un profileur .
10 fois plus rapide que les autres méthodes
La méthode suivante pour effectuer un clone profond est:
Et la méthode ...
Pour une vitesse ultime, vous pouvez utiliser Nested MemberwiseClone pour effectuer une copie complète . C'est presque la même vitesse que la copie d'une structure de valeur, et est beaucoup plus rapide que (a) la réflexion ou (b) la sérialisation (comme décrit dans d'autres réponses sur cette page).
Notez que si vous utilisez Nested MemberwiseClone pour une copie complète , vous devez implémenter manuellement un ShallowCopy pour chaque niveau imbriqué de la classe et un DeepCopy qui appelle toutes lesdites méthodes ShallowCopy pour créer un clone complet. C'est simple: seulement quelques lignes au total, voir le code de démonstration ci-dessous.
Voici la sortie du code montrant la différence de performance relative pour 100 000 clones:
L'utilisation de Nested MemberwiseClone sur une classe presque aussi vite que la copie d'une structure, et la copie d'une structure est sacrément proche de la vitesse maximale théorique dont .NET est capable.
Pour comprendre comment faire une copie complète à l'aide de MemberwiseCopy, voici le projet de démonstration qui a été utilisé pour générer les temps ci-dessus:
Ensuite, appelez la démo depuis main:
Encore une fois, notez que si vous utilisez Nested MemberwiseClone pour une copie complète , vous devez implémenter manuellement un ShallowCopy pour chaque niveau imbriqué de la classe et un DeepCopy qui appelle toutes lesdites méthodes ShallowCopy pour créer un clone complet. C'est simple: seulement quelques lignes au total, voir le code de démonstration ci-dessus.
Types de valeurs vs types de références
Notez que lorsqu'il s'agit de cloner un objet, il y a une grande différence entre une " struct " et une " classe ":
Voir les différences entre les types de valeurs et les types de références .
Sommes de contrôle pour faciliter le débogage
Vraiment utile pour découpler de nombreux threads de nombreux autres threads
Un excellent cas d'utilisation de ce code consiste à alimenter des clones d'une classe ou d'une structure imbriquée dans une file d'attente, pour implémenter le modèle producteur / consommateur.
ConcurrentQueue
.Cela fonctionne extrêmement bien dans la pratique et nous permet de découpler de nombreux threads (les producteurs) d'un ou plusieurs threads (les consommateurs).
Et cette méthode est également incroyablement rapide: si nous utilisons des structures imbriquées, elle est 35 fois plus rapide que la sérialisation / désérialisation de classes imbriquées, et nous permet de tirer parti de tous les threads disponibles sur la machine.
Mise à jour
Apparemment, ExpressMapper est aussi rapide, sinon plus rapide, que le codage manuel tel que ci-dessus. Je devrais peut-être voir comment ils se comparent à un profileur.
la source
En général, vous implémentez l'interface ICloneable et implémentez Clone vous-même. Les objets C # ont une méthode MemberwiseClone intégrée qui effectue une copie superficielle qui peut vous aider pour toutes les primitives.
Pour une copie complète, il n'y a aucun moyen de savoir comment le faire automatiquement.
la source
Je l'ai également vu implémenté par la réflexion. Fondamentalement, il existait une méthode permettant de parcourir les membres d'un objet et de les copier de manière appropriée vers le nouvel objet. Quand il a atteint des types de référence ou des collections, je pense qu'il a fait un appel récursif sur lui-même. La réflexion coûte cher, mais elle a plutôt bien fonctionné.
la source
Voici une implémentation de copie approfondie:
la source
Comme je n'ai pas pu trouver un cloneur qui réponde à toutes mes exigences dans différents projets, j'ai créé un cloneur profond qui peut être configuré et adapté à différentes structures de code au lieu d'adapter mon code pour répondre aux exigences des cloneurs. Son atteint en ajoutant des annotations au code qui doit être cloné ou vous laissez simplement le code tel qu'il est d'avoir le comportement par défaut. Il utilise la réflexion, saisit des caches et est basé sur un fléchissement plus rapide . Le processus de clonage est très rapide pour une énorme quantité de données et une hiérarchie d'objets élevée (par rapport à d'autres algorithmes basés sur la réflexion / sérialisation).
https://github.com/kalisohn/CloneBehave
Également disponible sous forme de package nuget: https://www.nuget.org/packages/Clone.Behave/1.0.0
Par exemple: Le code suivant va deepClone Address, mais effectue uniquement une copie superficielle du champ _currentJob.
la source
Générateur de code
Nous avons vu beaucoup d'idées allant de la sérialisation à l'implémentation manuelle en passant par la réflexion et je veux proposer une approche totalement différente en utilisant le générateur de code CGbR . La méthode de génération de clone est efficace en mémoire et en processeur et donc 300 fois plus rapide que le DataContractSerializer standard.
Tout ce dont vous avez besoin est une définition de classe partielle avec
ICloneable
et le générateur fait le reste:Remarque: La dernière version a une vérification plus nulle, mais je les ai laissés pour une meilleure compréhension.
la source
J'aime les Copyconstructors comme ça:
Si vous avez plus de choses à copier, ajoutez-les
la source
Cette méthode a résolu le problème pour moi:
Utilisez-le comme ceci:
MyObj a = DeepCopy(b);
la source
Voici une solution simple et rapide qui a fonctionné pour moi sans relayer sur Sérialisation / Désérialisation.
EDIT : nécessite
Voilà comment je l'ai utilisé
la source
Suivez ces étapes:
ISelf<T>
avec uneSelf
propriété en lecture seule qui renvoieT
, etICloneable<out T>
, qui dériveISelf<T>
et inclut une méthodeT Clone()
.CloneBase
type qui implémente uneprotected virtual generic VirtualClone
conversionMemberwiseClone
vers le type transmis.VirtualClone
en appelant la méthode de clone de base, puis en faisant tout ce qui doit être fait pour cloner correctement les aspects du type dérivé que la méthode VirtualClone parent n'a pas encore pris en charge.Pour une polyvalence d'héritage maximale, les classes exposant la fonctionnalité de clonage public doivent l'être
sealed
, mais dérivent d'une classe de base qui est par ailleurs identique, sauf pour l'absence de clonage. Plutôt que de passer des variables du type clonable explicite, prenez un paramètre de typeICloneable<theNonCloneableType>
. Cela permettra à une routine qui attend qu'un dérivé clonable deFoo
fonctionne avec un dérivé clonable deDerivedFoo
, mais permettra également la création de dérivés non clonables deFoo
.la source
Je pense que vous pouvez essayer ça.
la source
J'ai créé une version de la réponse acceptée qui fonctionne à la fois avec «[Serializable]» et «[DataContract]». Cela fait un moment que je ne l'ai pas écrit, mais si je me souviens bien, [DataContract] avait besoin d'un sérialiseur différent.
Nécessite System, System.IO, System.Runtime.Serialization, System.Runtime.Serialization.Formatters.Binary, System.Xml ;
la source
Ok, il y a un exemple évident de réflexion dans ce post, MAIS la réflexion est généralement lente, jusqu'à ce que vous commenciez à la mettre en cache correctement.
si vous le cachez correctement, alors il clone en profondeur l'objet 1000000 par 4,6s (mesuré par Watcher).
que vous prenez des propriétés en cache ou ajoutez de nouveaux au dictionnaire et utilisez-les simplement
vérification complète du code dans mon message dans une autre réponse
https://stackoverflow.com/a/34365709/4711853
la source
prop.GetValue(...)
est toujours une réflexion et ne peut pas être mis en cache. Dans un arbre d'expression, il est compilé, donc plus viteComme presque toutes les réponses à cette question ont été insatisfaisantes ou ne fonctionnent manifestement pas dans ma situation, j'ai écrit AnyClone qui est entièrement mis en œuvre avec réflexion et résolu tous les besoins ici. Je n'ai pas réussi à faire fonctionner la sérialisation dans un scénario compliqué avec une structure complexe, et ce
IClonable
n'est pas idéal - en fait, cela ne devrait même pas être nécessaire.Attributs ignorer standard sont pris en charge à l' aide
[IgnoreDataMember]
,[NonSerialized]
. Prend en charge les collections complexes, les propriétés sans setters, les champs en lecture seule, etc.J'espère que cela aide quelqu'un d'autre qui a rencontré les mêmes problèmes que moi.
la source
Avertissement: je suis l'auteur du package mentionné.
J'ai été surpris de voir comment les meilleures réponses à cette question en 2019 utilisent toujours la sérialisation ou la réflexion.
La sérialisation est limitative (nécessite des attributs, des constructeurs spécifiques, etc.) et est très lente
BinaryFormatter
requiert l'Serializable
attribut,JsonConverter
nécessite un constructeur ou des attributs sans paramètre, ni ne gère très bien les champs ou les interfaces en lecture seule et les deux sont 10-30x plus lents que nécessaire.Arbres d'expression
Vous pouvez à la place utiliser des arborescences d'expression ou Reflection.Emit pour générer du code de clonage une seule fois, puis utiliser ce code compilé au lieu d'une réflexion lente ou d'une sérialisation.
Ayant moi-même rencontré le problème et ne voyant aucune solution satisfaisante, j'ai décidé de créer un package qui fait exactement cela et qui fonctionne avec tous les types et qui est presque aussi rapide qu'un code écrit personnalisé .
Vous pouvez trouver le projet sur GitHub: https://github.com/marcelltoth/ObjectCloner
Usage
Vous pouvez l'installer depuis NuGet. Soit obtenez le
ObjectCloner
package et utilisez-le comme:ou si cela ne vous dérange pas de polluer votre type d'objet avec des extensions, obtenez
ObjectCloner.Extensions
aussi et écrivez:Performance
Un simple test de clonage d'une hiérarchie de classes a montré des performances ~ 3 fois plus rapides qu'avec Reflection, ~ 12 fois plus rapides que la sérialisation Newtonsoft.Json et ~ 36 fois plus rapides que celles fortement suggérées
BinaryFormatter
.la source