Profonde clonage des objets
Je veux faire quelque chose comme:
MyObject myObj = GetMyObj(); //Create and fill a new object
MyObject newObj = myObj.Clone();
Et apporter des modifications pour le nouvel objet qui ne sont pas reflétés dans l'objet d'origine.
Je n'ai pas souvent besoin de cette fonctionnalité, donc quand il a été nécessaire, j'ai eu recours à la création d'un nouvel objet, puis de le copier chaque propriété individuellement, mais il me laisse toujours avec le sentiment qu'il existe une meilleure ou la plus élégante façon de gérer la situation.
Comment puis-je cloner ou copie en profondeur d'un objet, de sorte que l'objet cloné peut être modifiée sans changements, qui se reflète dans l'objet d'origine?
- Peut être utile: "Pourquoi la Copie d'un Objet est une chose terrible à faire?" agiledeveloper.com/articles/cloning072002.htm
- stackoverflow.com/questions/8025890/... une Autre solution...
- Vous devriez jeter un oeil à AutoMapper
- Votre solution est beaucoup plus complexe, j'ai perdu le lire... hehehe. Je suis à l'aide d'un DeepClone interface. public interface IDeepCloneable<T> { T DeepClone(); }
- Une préoccupation que j'ai, avec
IDeepCloneable
est que pas toutes les collections de références à des choses qui peuvent être profondément clonés doivent être; le bon comportement lors du clonage d'unList<T>
ne dépend pas seulement surT
, mais aussi sur le but de la liste. Si aucun des éléments dans les listes seront jamais exposés à tout ce qui muter, alors même si les éléments dans les listes pourrait être cloné, il serait préférable de copier les références directement. - Cette question est également abordée ici stackoverflow.com/q/129389/235715
- Cependant, il est intéressant de noter que l'article finit par dire à créer un
clone
méthode de la classe, puis de l'appel interne, constructeur privé qui est passéethis
. Si la copie est turrible [sic], mais la copie soigneusement (et l'article est certainement la peine de lire) ne l'est pas. ;^) - Si vous avez besoin de ce vous avez peut-être mal mise en œuvre. Et si vous utiliser l'injection de dépendance, il ne fait pas de sens.
- À cette fin, à cette question, toutes les réponses sont à peu près aussi utile que "Comment puis-je code une classe?" Il y a beaucoup de réponses, mais il n'y a pas qu'une seule bonne réponse, malgré les votes. Ce n'est PAS à dire qu'aucune réponse n'est utile ou la question n'est pas le travail lui demander, mais méfiez-vous des lunettes polarisantes réponses. Le plus grand déficit d'ici l'accent est mis sur la fourniture d'une documentation détaillée et pour l'utilisateur/responsable de l'implémentation d'une classe de prendre la responsabilité de comprendre les détails de l'opération de copie.
- Pourquoi ne pas simplement obtenir une nouvelle instance? Ou si vous voulez copier un objet que vous avez modifié plutôt que de simplement instancié vous pouvez ainsi créer une méthode qui fait tout cela, et il suffit d'appeler la méthode à deux reprises.
- Appelez le MemberwiseClone méthode pour créer une copie d'un objet, puis affecter les nouveaux objets dont les valeurs sont les mêmes que l'original, objet de toutes les propriétés ou les champs dont les valeurs sont des types référence. Le Propriétédeepcopy méthode dans l'exemple qui illustre cette approche. msdn.microsoft.com/en-us/library/...
- Cochez cette Répondre: stackoverflow.com/a/52097307/4707576 à propos de: le Clonage des objets sans Sérialisation
Vous devez vous connecter pour publier un commentaire.
Alors que la norme est de mettre en œuvre la
ICloneable
interface (décrit ici, donc je ne vais pas régurgiter), voilà une belle profondeur cloner un objet copieur j'ai trouvé sur Le Projet De Code il y a un moment et l'a incorporé dans nos affaires.Comme mentionné ailleurs, il n'a besoin de vos objets sérialisables.
L'idée est qu'il sérialise votre objet, puis désérialise dans une nouvelle objet. L'avantage est que vous n'avez pas à vous soucier à propos du clonage tout quand un objet devient trop complexe.
Et à l'utilisation de méthodes d'extension (également à partir de l'origine référencé source):
Dans le cas où vous préférez utiliser la nouvelle les méthodes d'extension de C# 3.0, changer la méthode pour avoir la signature suivante:
Maintenant l'appel de la méthode devient tout simplement
objectBeingCloned.Clone();
.MODIFIER (10 janvier 2015) j'ai Pensé revoir ce, pour n'en citer j'ai récemment commencé à utiliser (Newtonsoft) Json pour ce faire, il devrait être plus léger, et évite la surcharge de [Serializable] tags. (NB @atconway l'a souligné dans les commentaires que les membres privés ne sont pas clonés à l'aide de la méthode JSON)
typeof(T).IsSerializable
est également vrai si le type a été marquée par la[Serializable]
attribut. Il n'a pas à mettre en œuvre lesISerializable
interface.[NonSerialized]
(ou[field: NonSerialized]
) pour que cela fonctionne.Serializable
soit. Dans ce cas, vous auriez besoin d'une solution qui utilise la réflexion.Newtonsoft.Json
approche. Il ne sera pas le clone de membres privés. J'ai été en utilisant cette méthode pendant un certain temps, quelqu'un m'a défié sur cet inconvénient, je l'ai testé pour voir, et bien sûr, il ne sera pas cloner les membres privés de l'objet source.Newtonsoft.Json
public bool Thing { get; internal set; }
. (propriétés comme ceux-ci sont ajoutés si vous ajoutez-les par "actions rapides") alors qu'ils ne sont pas être cloné avec cette méthode. Solution facile si!MemberwiseClone
crée une copie superficielle comme indiqué dans la documentation: docs.microsoft.com/en-us/dotnet/api/...Je voulais un cloneur pour des objets très simples de la plupart des primitives et des listes. Si votre objet est en dehors de la zone de JSON serializable cette méthode fera l'affaire. Cela ne nécessite aucune modification ou la mise en œuvre des interfaces sur la cloné classe, juste un sérialiseur JSON comme JSON.NET.
Aussi, vous pouvez utiliser cette méthode d'extension
Newtonsoft.Json.JsonConvert
mais c'est le mêmenew JsonSerializerSettings {ObjectCreationHandling = ObjectCreationHandling.Replace}
, parce que le constructeur par défaut peut changer quelque chose qui n'est pas dans la sourceLa raison de ne pas utiliser ICloneable est pas car il n'a pas une interface générique. La raison de ne pas l'utiliser parce que c'est vague. Il ne fait pas clair de savoir si vous obtenez un plat peu profond ou d'une copie en profondeur; c'est à l'oeuvre.
Oui,
MemberwiseClone
fait une copie superficielle, mais à l'opposé deMemberwiseClone
n'est pasClone
; il serait, peut-être,DeepClone
, qui n'existe pas. Lorsque vous utilisez un objet par le biais de son interface ICloneable, vous ne savez pas quel type de clonage de l'objet sous-jacent effectue. (Et des commentaires XML ne le fera pas clair, parce que vous obtiendrez l'interface des commentaires plutôt que sur l'objet de la méthode Clone.)Ce que j'ai l'habitude de faire est de simplement faire une
Copy
méthode qui fait exactement ce que je veux.Après beaucoup de lecture sur la plupart des options de lien ici, et les solutions possibles pour cette question, je crois que toutes les options sont résumées assez bien le Ian P's lien (toutes les autres options sont des variantes de celles-ci) et la meilleure solution est fournie par Pedro77's lien sur la question des commentaires.
Donc je vais juste copier les parties pertinentes de ces 2 références ici. De cette façon, nous pouvons avoir:
La meilleure chose à faire pour le clonage des objets en C sharp!
D'abord et avant tout, ce sont toutes nos options:
La article Rapide Copie en Profondeur par des Arbres d'Expression a également de comparer la performance de clonage par la Sérialisation, la Réflexion et l'Expression des Arbres.
Pourquoi j'ai choisi ICloneable (c'est à dire manuellement)
M. Venkat Subramaniam (lien redondant ici) explique en détail pourquoi.
Tous son article cercles autour d'un exemple qui essaie d'être applicable dans la plupart des cas, à l'aide de 3 objets: Personne, Cerveau et Ville. Nous voulons clone d'une personne, qui aura son propre cerveau, mais la même ville. Vous pouvez soit l'image de tous les problèmes de l'une des méthodes ci-dessus peuvent apporter ou de lire l'article.
C'est ma version légèrement modifiée de sa conclusion:
Nous espérons que cette mise en œuvre peut mettre les choses au clair:
Maintenant envisager d'avoir une classe de dériver de Personne.
Vous pouvez essayer d'exécuter le code suivant:
La sortie produite sera:
Observer que, si nous tenons compte du nombre d'objets, le clone mis en œuvre ici gardera un bon de compter le nombre d'objets.
ICloneable
pour les membres du public. "Parce que les appelants de Clone ne peut dépendre de la méthode de l'exécution d'une prévisible de l'opération de clonage, nous vous recommandons de ICloneable pas être mis en œuvre dans les Api publiques." msdn.microsoft.com/en-us/library/... Cependant, d'après l'explication donnée par Venkat Subramaniam dans votre article lié, je pense qu'il est logique de l'utiliser dans cette situation tant que les créateurs de la ICloneable les objets ont une profonde compréhension des propriétés qui doit être profond vs peu profondes des copies (c'est à dire copie en profondeur du Cerveau, la copie superficielle de la Ville)ICloneable
, il est plus logique de définir votre propre interface et des méthodes pour faire exactement ce que vous voulez. Ensuite, vous pouvez documenter clairement dans les commentaires XML précisément ce type de clonage et de la copie qu'il fait. Par ailleurs, dans certains de 15 ans .NET dans une variété d'emplois et d'industries, j'ai presque jamais eu besoin de clone/copie de quoi que ce soit.Je préfère un constructeur de copie pour un clone. L'intention est claire.
AbstractBaseType
, avec 3 types dérivés, chacun étant tels queType1:AbstractBaseType
a unType2:AbstractBaseType
membre, qui a unType3:AbstractBaseType
membre. Maintenant, vous devez vérifier les types.DataContract
avec 50+ champs. Imaginez que vous allez à ajouter un nouveau champ, ou dans les champs. Il peut devenir un cluster-f* réel rapide. Toute modification que vous apportez à la classe, vous devez également vous rappeler de faire pour le constructeur de copie. C'est pourquoi il est préférable de laisser la copie jusqu'à quelque chose comme la sérialisation ou de la réflexion. Ce serait bien si C# serait d'y ajouter la fonctionnalité de Copie en Profondeur elle-même, mais je crois qu'il l'a quitté parce que la définition de ce qu'est une Copie en Profondeur n'est pas coupé et séchéSimple extension de la méthode pour copier toutes les propriétés publiques. Fonctionne pour tous les objets et les ne pas exiger de la classe
[Serializable]
. Peut être étendu à d'autres niveaux d'accès.Bien j'ai eu des problèmes à l'aide de ICloneable dans Silverlight, mais j'ai bien aimé l'idée de seralization, je peux seralize XML, j'ai donc fait ceci:
Je viens de créer
CloneExtensions
de la bibliothèque projet. Il effectue rapide, profonde clone à l'aide d'une simple affectation des opérations générées par l'Expression de l'Arbre d'exécution de la compilation du code.Comment l'utiliser?
Au lieu d'écrire votre propre
Clone
ouCopy
méthodes avec un ton d'attributions entre les champs et les propriétés de rendre le programme de le faire pour vous-même, à l'aide de l'Expression de l'Arbre.GetClone<T>()
méthode marqué comme méthode d'extension vous permet de simplement appeler votre instance:Vous pouvez choisir ce qui doit être copié à partir de
source
ànewInstance
à l'aide deCloningFlags
enum:Ce qui peut être cloné?
types (DateTime, laps de Temps (la Chaîne) et les délégués (y compris
Action, Func, etc)
Suivants de la classe/les membres de la structure sont clonés à l'interne:
Comment il est rapide?
La solution est plus rapide que la réflexion, parce que les membres de l'information doit être recueillie une seule fois, avant de
GetClone<T>
est utilisé pour la première fois pour le type donnéT
.Il est également plus rapide que la sérialisation à base de solution lorsque vous clonez plus de couple instances d'un même type
T
.et plus...
Lire plus sur générés expressions sur la documentation.
Exemple d'expression de débogage de liste pour
List<int>
:}
ce qui a le même sens comme code c# suivant:
N'est-il pas tout à fait comme la façon dont vous voulez écrire votre propre
Clone
méthode pourList<int>
?Si vous êtes déjà à l'aide d'une application 3ème partie comme ValueInjecter ou Automapper, vous pouvez faire quelque chose comme ceci:
Utilisant cette méthode, vous n'avez pas à mettre en œuvre ISerializable ou ICloneable sur vos objets. Cette situation est commune avec le MVC/pattern MVVM, de sorte que de simples outils de ce genre ont été créés.
voir le valueinjecter profonde clonage solution sur CodePlex.
Le mieux est de mettre en œuvre un méthode d'extension comme
et ensuite l'utiliser n'importe où dans la solution par
Nous pouvons avoir trois implémentations:
Tous liés les méthodes de travail bien et ont été profondément testé.
La réponse courte est que vous hériter de l'interface ICloneable et de mettre en œuvre l' .clone de la fonction. Clone devrait faire un memberwise copie et d'effectuer une copie en profondeur sur n'importe quel membre qui en a besoin, puis retourner l'objet résultant. C'est une opération récursive ( il faut que tous les membres de la classe que vous voulez cloner sont soit des types de valeur ou de mettre en œuvre ICloneable et que leurs membres sont soit des types de valeur ou de mettre en œuvre ICloneable, et ainsi de suite).
Pour une explication plus détaillée sur le Clonage à l'aide de ICloneable, découvrez cet article.
La long réponse est "ça dépend". Comme mentionné par d'autres, ICloneable n'est pas pris en charge par les génériques, exige des considérations spéciales pour la circulaire références de classe, et est en fait considéré par certains comme un "erreur" dans le .NET Framework. La méthode de sérialisation dépend de vos objets sérialisables, qu'ils ne peuvent pas être et vous n'avez pas le contrôle. Il y a encore beaucoup de débats dans la communauté au cours de laquelle est la "meilleure pratique". En réalité, aucun de ces solutions sont de la taille unique, s'adapte à tous les meilleures pratiques pour toutes les situations comme ICloneable était à l'origine interprété à être.
Voir ce Coin du développeur de l'article pour un peu plus d'options (crédit à Ian).
Acclamations.
EDIT: le projet est abandonné
Si vous voulez un vrai clonage de types inconnus, vous pouvez prendre un coup d'oeil à
fastclone.
Que l'expression de la fonction de clonage de travail environ 10 fois plus rapide que la sérialisation binaire et maintenant l'objet graphique de l'intégrité.
Qui signifie: si vous consultez plusieurs fois pour le même objet dans votre hiérarchie, le clone aura aussi la seule instance à avoir référencé.
Il n'est pas nécessaire pour les interfaces, les attributs ou de toute autre modification des objets clonés.
Garder les choses simples et AutoMapper comme d'autres l'ont mentionné, c'est une simple petite bibliothèque à la carte un objet à l'autre... Pour copier un objet à un autre de même type, vous n'avez besoin que de trois lignes de code:
L'objet cible est maintenant une copie de l'objet source.
Pas assez simple? Créer une extension de la méthode à utiliser partout dans votre solution:
À l'aide de la méthode d'extension, les trois lignes deviennent une seule ligne:
Je suis venu avec cette pour surmonter une .NET lacune manuellement deep copy List<T>.
J'utilise ceci:
Et à un autre endroit:
J'ai essayé de venir avec oneliner qui fait cela, mais il n'est pas possible, en raison du rendement ne fonctionne pas à l'intérieur de méthode anonyme blocs.
Mieux encore, utilisez générique List<T> cloner:
Q. Pourquoi ai-je choisi cette réponse?
En d'autres termes, aller avec une autre réponse, sauf si vous avez un goulot d'étranglement des performances qui doit être réparé, et vous pouvez le prouver avec un profiler.
10x plus rapide que les autres méthodes
La méthode suivante de l'exécution d'une profonde clone est:
Et la méthode ...
Pour la vitesse ultime, vous pouvez utiliser Imbriquée MemberwiseClone faire une copie en profondeur. Ses presque la même vitesse que la copie d'une valeur de struct, et est beaucoup plus rapide que (un) ou de réflexion (b) la sérialisation (comme décrit dans d'autres réponses sur cette page).
Noter que si vous utilisez Imbriquée MemberwiseClone pour une copie en profondeur, vous devez manuellement mettre en œuvre un ShallowCopy pour chaque niveau imbriqué dans la classe, et un Propriétédeepcopy qui appelle tous dit ShallowCopy méthodes pour créer un clone complet. C'est simple: seulement quelques lignes, au total, voir la démo de code ci-dessous.
Ici est la sortie du code montrant la performance relative de différence pour 100 000 clones:
Utilisant des MemberwiseClone sur une classe presque aussi rapide que la copie d'une struct, et la copie d'un struct est vachement proche de la vitesse théorique maximale .NET est capable de.
À comprendre comment faire une copie en profondeur à l'aide de MemberwiseCopy, ici, c'est le projet de démonstration qui a été utilisé pour générer la fois ci-dessus:
Ensuite, appelez la démo principal:
À nouveau, notez que si vous utilisez Imbriquée MemberwiseClone pour une copie en profondeur, vous devez manuellement mettre en œuvre un ShallowCopy pour chaque niveau imbriqué dans la classe, et un Propriétédeepcopy qui appelle tous dit ShallowCopy méthodes pour créer un clone complet. C'est simple: seulement quelques lignes, au total, voir la démo de code ci-dessus.
Types de valeur vs Références Types
Remarque que quand il s'agit de clonage d'un objet, il y a une grande différence entre un "struct" et un "classe":
Voir les différences entre les types valeur et les types de références.
Les sommes de contrôle pour faciliter le débogage
Vraiment utile pour le découplage nombre de threads que de nombreux autres threads
Un excellent cas d'utilisation de ce code est l'alimentation des clones d'une classe imbriquée ou struct dans une file d'attente, pour mettre en œuvre le producteur /consommateur motif.
ConcurrentQueue
.Cela fonctionne extrêmement bien dans la pratique, et nous permet de découpler le nombre de threads (les producteurs) à partir d'un ou plusieurs threads (les consommateurs).
Et cette méthode est absolument trop rapide: si nous utilisons des structures imbriquées, c'est 35x plus vite que la sérialisation/désérialisation des classes imbriquées, et nous permet de profiter de tous les threads disponibles sur la machine.
Mise à jour
Apparemment, ExpressMapper est aussi rapide, sinon plus, que le codage à la main comme ci-dessus. Je pourrais l'avoir pour voir comment ils se comparent avec un profiler.
En général, vous mettez en œuvre l'interface ICloneable et de mettre en œuvre Clone de vous-même.
C# objets ont un MemberwiseClone méthode qui effectue une copie superficielle qui peut vous aider pour toutes les primitives.
Pour une copie en profondeur, il n'y a aucun moyen de savoir comment automatiquement.
Je l'ai vu mis en œuvre par le biais de la réflexion en tant que bien. Fondamentalement, il y avait une méthode qui permettrait d'itérer sur les membres d'un objet et la manière appropriée de les copier sur le nouvel objet. Quand il atteint les 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 est cher, mais il a fonctionné assez bien.
Ici est une copie en profondeur la mise en œuvre:
Que je ne pouvais pas trouver un cloneur qui répond à toutes mes exigences dans les différents projets, j'ai créé une profonde cloner qui peuvent être configurés et adaptés aux différentes structures de code au lieu d'adapter mon code pour répondre aux cloneurs exigences. Son obtenu par ajout d'annotations dans le code qui doit être cloné ou que vous venez de laisser le code comme il est d'avoir le comportement par défaut. Il utilise la réflexion, type de caches et est basé sur fasterflect. Le processus de clonage est très rapide pour une énorme quantité de données et d'une haute hiérarchie de l'objet (par rapport à d'autres de réflexion/de sérialisation de base des algorithmes).
https://github.com/kalisohn/CloneBehave
Également disponible comme un package nuget:
https://www.nuget.org/packages/Clone.Behave/1.0.0
Par exemple: Le code suivant deepClone Adresse, mais seulement d'effectuer une copie de la _currentJob champ.
Cette méthode a résolu le problème pour moi:
L'utiliser comme ceci:
MyObj a = DeepCopy(b);
J'aime Copyconstructors comme ça:
Si vous avez plus de choses à copier les ajouter
Générateur De Code
Nous avons vu beaucoup d'idées de sérialisation manuel de mise en œuvre de la réflexion et je veux proposer une approche totalement différente à l'aide de la CGbR Générateur De Code. L'générer de la méthode clone est la mémoire et le PROCESSEUR efficace et à cet effet, 300x plus rapide que la norme DataContractSerializer.
Tous vous avez besoin est une définition de classe partielle avec
ICloneable
et le générateur se charge du reste:Remarque: version la plus récente a plus nulle vérifie, mais je l'ai quitté pour une meilleure compréhension.
Ici une solution rapide et facile qui a fonctionné pour moi sans relais sur la Sérialisation/Désérialisation.
MODIFIER:
nécessite
C'est la Façon dont je l'ai utilisé
Je pense que vous pouvez l'essayer.
Suivez ces étapes:
ISelf<T>
avec un accès en lecture seuleSelf
propriété qui renvoieT
, etICloneable<out T>
, qui dérive deISelf<T>
et inclut une méthodeT Clone()
.CloneBase
type qui implémente unprotected virtual generic VirtualClone
castingMemberwiseClone
pour le type transmis.VirtualClone
par l'appel de la base de méthode clone et ensuite de faire ce qui doit être fait correctement cloner ces aspects du type dérivé dont le parent VirtualClone méthode n'a pas encore traitées.Pour un maximum de l'héritage de la polyvalence, des classes d'exposer public clonage fonctionnalité doit être
sealed
, mais dérivent d'une classe de base qui est d'ailleurs identique à l'exception de l'absence de clonage. Plutôt que de passer des variables de l'explicite clonable type, prend un paramètre de typeICloneable<theNonCloneableType>
. Cela permettra à une routine qui s'attend à un clonable dérivés deFoo
de travailler avec un clonable dérivés deDerivedFoo
, mais également permettre la création de non-clonable dérivés deFoo
.J'ai créé une version de la accepté de répondre qui fonctionne à la fois "[Serializable] " et " [DataContract]'. Il a été un moment depuis que je l'ai écrit, mais si je me souviens bien [DataContract] besoin d'un sérialiseur différent.
Nécessite Système De Système De.IO, Système.Moment de l'exécution.La Sérialisation, Système.Moment de l'exécution.La sérialisation.Des formateurs.Binaire, System.Xml;
Pour cloner votre objet de classe, vous pouvez utiliser l'Objet.MemberwiseClone méthode,
il suffit d'ajouter cette fonction à votre classe :
ensuite d'effectuer une profonde indépendant de la copie, il suffit d'appeler le Propriétédeepcopy méthode :
espère que cette aide.
Ok, il y a quelques exemple évident avec la réflexion dans ce post, MAIS la réflexion est généralement lente, jusqu'à ce que vous commencez à cache correctement.
si vous cache correctement, que ça va profond clone 1000000 objet en 4,6 s (mesurée par l'Observateur).
que vous prenez en cache des propriétés ou en ajouter de nouvelles pour dictionnaire et de les utiliser simplement
complet de vérification de code dans mon post dans une autre réponse
https://stackoverflow.com/a/34365709/4711853
prop.GetValue(...)
est encore à la réflexion et ne peut pas être mis en cache. Dans une arborescence d'expression de ses compilé, donc plus rapideSi votre Objet est un Arbre Serializeable vous pouvez également utiliser quelque chose comme ceci
être informé que cette Solution est assez facile, mais il n'est pas aussi performante que d'autres solutions peuvent être.
Et assurez-vous que si la Classe se développe, il y aura toujours uniquement les champs cloné, qui par ailleurs sont sérialisés.
Comme presque toutes les réponses à cette question ont été insatisfaisants ou simplement ne fonctionnent pas dans ma situation, j'ai rédigé AnyClone qui est entièrement mis en œuvre, de réflexion, de résoudre tous les besoins. Je n'ai pu obtenir de sérialisation de travailler dans un scénario compliqué à structure complexe, et
IClonable
est pas l'idéal en fait, il ne devrait même pas être nécessaire.Standard ignorer les attributs sont pris en charge à l'aide de
[IgnoreDataMember]
,[NonSerialized]
. Prend en charge complexe des collections, propriétés sans setters, readonly les champs, etc.J'espère que cela aide quelqu'un d'autre qui a couru dans les mêmes problèmes que moi.
Disclaimer: je suis l'auteur de ce forfait.
J'ai été surpris de voir comment les principales réponses à cette question en 2019 toujours utiliser la sérialisation ou de la réflexion.
La sérialisation est limitant (nécessite attributs spécifiques des constructeurs, etc.) et il est très lent
BinaryFormatter
nécessite laSerializable
attribut,JsonConverter
nécessite un constructeur sans paramètre ou d'attributs, ni la poignée de lire que des champs ou des interfaces très bien et les deux sont de 10-30x plus lentement que nécessaire.Des Arbres D'Expression
Place, vous pouvez utiliser des Arbres d'Expression ou Réflexion.Émettent pour générer le clonage code qu'une seule fois, puis utiliser le code compilé à la place de la lenteur de la réflexion ou de la sérialisation.
Avoir trouver le problème moi-même et ne voyant pas de solution satisfaisante, j'ai décidé de créer un package qui n'est juste que et fonctionne avec tous les types et est presque aussi rapide que la coutume le code écrit.
Vous pouvez trouver le projet sur GitHub: https://github.com/marcelltoth/ObjectCloner
Utilisation
Vous pouvez l'installer à partir de NuGet. Obtenir soit l'
ObjectCloner
paquet et l'utiliser comme:ou si vous n'avez pas l'esprit de polluer votre type d'objet avec les extensions
ObjectCloner.Extensions
ainsi et à écrire:Performance
Un simple indice de référence du clonage d'une hiérarchie de classe a montré des performances ~3x plus rapide que l'utilisation de la Réflexion, ~12x plus rapide que Newtonsoft.La sérialisation Json et ~36 fois plus rapides que la très suggéré
BinaryFormatter
.C'est incroyable de voir comment beaucoup d'effort, vous pouvez passer avec IClonable interface - surtout si vous avez des hiérarchies de classes. Aussi MemberwiseClone fonctionne en quelque sorte bizarrement elle ne correspond pas exactement clone de même normal de la Liste de type de type de structures.
Et bien sûr le plus intéressant dilemme pour la sérialisation est de sérialiser les références arrières - par exemple, les hiérarchies de classe où vous avez enfant-parent relations.
Je doute que le binaire sérialiseur sera en mesure de vous aider dans ce cas. (Il va finir avec des boucles récursives + débordement de pile).
J'ai un peu aimé solution proposée ici: Comment voulez-vous faire une copie d'un objet .NET (C# en particulier)?
cependant il n'est pas fait des Listes, a ajouté que le soutien, a également pris en compte de la parentalité.
Pour les parents seule règle que j'ai faite ce domaine ou bien devrait être nommé "parent", il sera ignoré par DeepClone. Vous pourriez décider de vos propres règles pour l'arrière-références - pour arbre hiérarchies de "gauche/droite", etc...
Ici est toute l'extrait de code, y compris le code de test:
Lors de l'utilisation de Marc Gravells protobuf-réseau que votre sérialiseur de la accepté de répondre à des besoins, de légères modifications, comme l'objet à copier ne sera pas attribué à
[Serializable]
et, par conséquent, n'est pas sérialisable et le Clone-méthode lève une exception.Je l'ai modifié pour qu'il fonctionne avec protobuf-net:
Ce vérifie la présence d'un
[ProtoContract]
attribut et utilise protobufs propre formateur pour sérialiser l'objet.Encore un autre JSON.NET réponse. Cette version fonctionne avec des classes qui n'implémentent pas de ISerializable.
C# Extension qui va de soutien pour la "pas ISerializable" types trop.
Utilisation
Le générique approches sont tous techniquement valide, mais je voulais juste ajouter une note de moi-même, depuis que nous avons rarement besoin d'une vraie copie en profondeur, et je vous encourage vivement à s'opposer à l'utilisation de générique profonde de la copie de réelles applications d'entreprise depuis qui le rend si vous pourriez avoir beaucoup d'endroits où les objets sont copiés et modifiés de façon explicite, il est facile de se perdre.
Dans la plupart des situations de la vie réelle aussi, vous voulez avoir un contrôle sélectif sur le processus de copie que possible, car vous n'êtes pas seulement couplé à l'accès aux données de cadre, mais aussi dans la pratique du copié business objects est rarement de 100% le même. Pensez à un exemple referenceId est utilisé par l'ORM pour identifier les références de l'objet, une profonde et complète de la copie sera également copier ce code est ainsi, alors en mémoire les objets sont différents, dès que vous envoyez à la banque de données, il va se plaindre, alors vous devrez modifier manuellement les propriétés de cette après la copie de toute façon et si l'objet de modifications dont vous avez besoin pour l'adapter à tous les lieux où l'utilisation de la générique profonde de la copie.
Expansion sur @cregox réponse avec ICloneable, ce qui est en fait une copie en profondeur? C'est juste un nouvellement objet alloué sur le tas qui est identique à l'objet original, mais occupe une mémoire différente de l'espace, en tant que tel plutôt que d'utiliser un générique cloner fonctionnalité pourquoi ne pas simplement créer un nouvel objet?
Personnellement, j'utilise l'idée de la statique des méthodes de fabrique sur mon domaine d'objets.
Exemple:
Si quelqu'un est à la recherche de comment il est possible de structurer l'instanciation d'objets tout en conservant le plein contrôle sur le processus de copie qui est une solution que j'ai été personnellement très réussi avec. Le protégé de constructeurs également faire en sorte, d'autres développeurs sont obligés d'utiliser les méthodes de fabrique qui donne un joli point unique de l'instanciation d'objets encapsulant la construction logique à l'intérieur de l'objet. Vous pouvez aussi surcharger la méthode et ont plusieurs clone de logic pour les différents lieux si nécessaire.
Un mappeur effectue une copie en profondeur. Foreach membre de votre objet, il crée un nouvel objet et de céder l'ensemble de ses valeurs. Il fonctionne de manière récursive sur chaque non-primitif intérieure membre.
Je vous suggère de l'un des plus rapides, actuellement activement développés.
Je suggère UltraMapper https://github.com/maurosampietro/UltraMapper
Les packages Nuget: https://www.nuget.org/packages/UltraMapper/
Cela va copier tous les de lecture et d'écriture des propriétés d'un objet à un autre.
et ce est la façon dont vous l'utiliser:
ou de tout copier:
que diriez-vous simplement la refonte à l'intérieur d'une méthode
que doit invoquer essentiellement une copie automatique constructeur
semble fonctionner pour moi
J'ai trouvé une nouvelle façon de faire qui est en Émettent.
Nous pouvons utiliser Émettent ajouter le IL à l'application et l'exécuter. Mais je ne pense pas que ses un bon moyen pour je veux perfectionner ce que j'écris ma réponse.
Le Émettent pouvez voir le document officiel et Guide
Vous devriez apprendre quelques IL de lire le code. Je vais écrire le code que vous pouvez copier à la propriété dans la classe.
Le code peut être copie en profondeur, mais il peut copier de la propriété. Si vous voulez faire de copie en profondeur que vous pouvez le modifier pour le IL est trop dur que je ne peux pas le faire.
Profonde clonage est à propos de la copie état. Pour
.net
état signifie champs.Disons un a une hiérarchie:
Le clonage peut être fait:
Demo1:
Résultat:
Avis, tous les nouveaux objets ont des valeurs aléatoires pour
random
champ, maisclone
correspond exactement à laimage
Demo2:
Résultat:
Avis, l'événement de la sauvegarde de champ est copié trop et le client est abonné à cloner l'événement de trop.
À l'aide de
System.Text.Json
:https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-apis/
La nouvelle API est à l'aide de
Span<T>
. Cela devrait être rapide, ce serait bien de faire quelques benchmarks.Remarque: il n'y a pas besoin de
ObjectCreationHandling.Replace
comme dans Json.NET comme il remplacera collection de valeurs par défaut. Vous devriez oublier Json.NET maintenant que tout va être remplacée par la nouvelle officielle de l'API.Je ne suis pas sûr que cela fonctionne avec des champs privés.
Rapide, facile, efficace package Nuget pour résoudre le Clonage
Après avoir lu toutes les réponses, j'ai été surpris personne n'a mentionné cet excellent package:
https://github.com/force-net/DeepCloner
Élaborer un peu sur son readme, voici la raison pour laquelle nous l'avons choisi au travail:
Avertissement - exigences:
Utilisation est simple:
Voie la plus courte, mais besoin de dépendance:
Je sais que cette question et réponse se trouve ici pendant un certain temps et la suite n'est pas tout à fait répondre, mais plutôt de l'observation, à laquelle je suis tombé récemment, lorsque j'ai été vérifier si en effet les soldats ne sont pas clonés (je ne serais pas moi-même si je n'ai pas 😉 quand j'ai été heureux de copie-collé @johnc mise à jour de réponse.
J'ai simplement fait moi-même une extension de la méthode (qui est assez bien copier coller le formulaire susmentionné réponse):
et a chuté naïvement classe comme ceci (en fait il n'y avait plus de personnes, mais ils ne sont pas liés):
et le code comme ceci:
abouti à:
J'ai été tout, comme: CE que LE F... j'ai donc pris la Newtonsoft.Json dépôt Github et a commencé à creuser.
Ce qu'il sort, c'est que: alors que la désérialisation d'un type qui arrive à avoir un seul ctor et de ses param noms de match (insensible à la casse) la propriété publique noms, ils seront transmis à ctor que ceux params. Certains indices peuvent être trouvés dans le code ici et ici.
Ligne de fond
Je sais que c'est plutôt pas commun de cas et d'exemples de code est un peu abusif, mais hey! Il m'a eu par surprise lorsque j'ai été vérifier s'il existe un dragon d'attente dans les buissons de sauter et de me mordre dans le cul. 😉