Quiero hacer algo como:
MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = myObj.Clone();
Y luego realice cambios en el nuevo objeto que no se reflejan en el objeto original.
A menudo no necesito esta funcionalidad, así que cuando ha sido necesario, he recurrido a crear un nuevo objeto y luego copiar cada propiedad individualmente, pero siempre me deja la sensación de que hay una forma mejor o más elegante de manejar la situación.
¿Cómo puedo clonar o copiar en profundidad un objeto para que el objeto clonado pueda modificarse sin que se refleje ningún cambio en el objeto original?
clone
método en la clase y luego se llame a un constructor interno y privado que se apruebethis
. Así que copiar es turrible [sic], pero copiar con cuidado (y el artículo definitivamente vale la pena leer) no lo es. ; ^)Respuestas:
Si bien la práctica estándar es implementar la
ICloneable
interfaz (descrita aquí , por lo que no regurgitaré), aquí hay una buena copiadora de objetos de clonación profunda que encontré en The Code Project hace un tiempo y la incorporé a nuestras cosas.Como se mencionó en otra parte, requiere que sus objetos sean serializables.
La idea es que serializa su objeto y luego lo deserializa en un objeto nuevo. El beneficio es que no tiene que preocuparse por clonar todo cuando un objeto se vuelve demasiado complejo.
Y con el uso de métodos de extensión (también de la fuente referenciada originalmente):
En caso de que prefiera utilizar los nuevos métodos de extensión de C # 3.0, cambie el método para tener la siguiente firma:
Ahora la llamada al método simplemente se convierte
objectBeingCloned.Clone();
.EDITAR (10 de enero de 2015) Pensé en volver a visitar esto, por mencionar que recientemente comencé a usar (Newtonsoft) Json para hacer esto, debería ser más liviano y evitar la sobrecarga de las etiquetas [Serializable]. ( NB @atconway ha señalado en los comentarios que los miembros privados no se clonan utilizando el método JSON)
fuente
typeof(T).IsSerializable
también es cierto si el tipo se ha marcado con el[Serializable]
atributo. No tiene que implementar laISerializable
interfaz.Quería un clonador para objetos muy simples, en su mayoría primitivos y listas. Si su objeto está fuera de la caja JSON serializable, entonces este método hará el truco. Esto no requiere modificación o implementación de interfaces en la clase clonada, solo un serializador JSON como JSON.NET.
Además, puede usar este método de extensión
fuente
Newtonsoft.Json.JsonConvert
pero es lo mismoLa razón para no usar ICloneable no es porque no tiene una interfaz genérica. La razón para no usarlo es porque es vago . No aclara si está obteniendo una copia superficial o profunda; eso depende del implementador.
Sí,
MemberwiseClone
hace una copia superficial, pero lo contrario deMemberwiseClone
no lo esClone
; sería, tal vezDeepClone
, lo que no existe. Cuando utiliza un objeto a través de su interfaz ICloneable, no puede saber qué tipo de clonación realiza el objeto subyacente. (Y los comentarios XML no lo dejarán claro, porque obtendrá los comentarios de la interfaz en lugar de los del método Clone del objeto).Lo que suelo hacer es simplemente crear un
Copy
método que haga exactamente lo que quiero.fuente
Después de leer mucho sobre muchas de las opciones vinculadas aquí, y las posibles soluciones para este problema, creo que todas las opciones se resumen bastante bien en el enlace de Ian P (todas las demás opciones son variaciones de esas) y la mejor solución es la que proporciona El enlace de Pedro77 sobre la pregunta comenta.
Así que solo copiaré partes relevantes de esas 2 referencias aquí. De esa manera podemos tener:
¡Lo mejor para clonar objetos en C sharp!
Ante todo, esas son todas nuestras opciones:
El artículo Fast Deep Copy de Expression Trees también tiene una comparación de rendimiento de la clonación por serialización, reflexión y árboles de expresión.
Por qué elijo ICloneable (es decir, manualmente)
El Sr. Venkat Subramaniam (enlace redundante aquí) explica con mucho detalle por qué .
Todo su artículo gira en torno a un ejemplo que intenta ser aplicable para la mayoría de los casos, utilizando 3 objetos: Persona , Cerebro y Ciudad . Queremos clonar a una persona, que tendrá su propio cerebro pero la misma ciudad. Puede imaginar todos los problemas que cualquiera de los otros métodos anteriores puede traer o leer el artículo.
Esta es mi versión ligeramente modificada de su conclusión:
Esperemos que esta implementación pueda aclarar las cosas:
Ahora considere tener una clase derivada de Persona.
Puede intentar ejecutar el siguiente código:
La salida producida será:
Observe que, si mantenemos un recuento del número de objetos, el clon implementado aquí mantendrá un recuento correcto del número de objetos.
fuente
ICloneable
para miembros públicos. "Debido a que las personas que llaman de Clone no pueden depender del método que realiza una operación de clonación predecible, recomendamos que ICloneable no se implemente en API públicas". msdn.microsoft.com/en-us/library/… Sin embargo, según la explicación dada por Venkat Subramaniam en su artículo vinculado, creo que tiene sentido usarlo en esta situación siempre que los creadores de los objetos ICloneable tengan un profundo comprensión de qué propiedades deberían ser copias profundas frente a copias superficiales (es decir, copia profunda Cerebro, copia superficial Ciudad)Prefiero un constructor de copia a un clon. La intención es más clara.
fuente
Método de extensión simple para copiar todas las propiedades públicas. Funciona para cualquier objeto y no requiere clase para ser
[Serializable]
. Se puede ampliar para otro nivel de acceso.fuente
Acabo de crear un proyecto de
CloneExtensions
biblioteca . Realiza una clonación rápida y profunda mediante operaciones de asignación simples generadas por la compilación del código de tiempo de ejecución de Expression Tree.¿Cómo usarlo?
En lugar de escribir sus propios métodos
Clone
oCopy
con un tono de asignaciones entre campos y propiedades, haga que el programa lo haga por usted mismo, usando Expression Tree.GetClone<T>()
El método marcado como método de extensión le permite simplemente llamarlo en su instancia:Puede elegir de qué se debe copiar
source
paranewInstance
usarCloningFlags
enum:¿Qué se puede clonar?
Los siguientes miembros de clase / estructura se clonan internamente:
¿Qué tan rápido es?
La solución es más rápida que la reflexión, porque la información de los miembros se debe recopilar solo una vez, antes de que
GetClone<T>
se use por primera vez para un tipo determinadoT
.También es más rápido que la solución basada en serialización cuando clonas más de dos instancias del mismo tipo
T
.y más...
Lea más sobre las expresiones generadas en la documentación .
Lista de depuración de expresiones de muestra para
List<int>
:}
lo que tiene el mismo significado como el siguiente código de C #:
¿No es como escribirías tu propio
Clone
métodoList<int>
?fuente
Bueno, estaba teniendo problemas para usar ICloneable en Silverlight, pero me gustó la idea de la seralización, puedo seralizar XML, así que hice esto:
fuente
Si ya está utilizando una aplicación de terceros como ValueInjecter o Automapper , puede hacer algo como esto:
Con este método no tiene que implementar
ISerializable
niICloneable
en sus objetos. Esto es común con el patrón MVC / MVVM, por lo que se han creado herramientas simples como esta.vea la muestra de clonación profunda ValueInjecter en GitHub .
fuente
Lo mejor es implementar un método de extensión como
y luego usarlo en cualquier lugar de la solución
Podemos tener las siguientes tres implementaciones:
Todos los métodos vinculados funcionan bien y se probaron profundamente.
fuente
La respuesta corta es que usted hereda de la interfaz ICloneable y luego implementa la función .clone. Clone debe hacer una copia de miembro y realizar una copia profunda en cualquier miembro que lo requiera, luego devolver el objeto resultante. Esta es una operación recursiva (requiere que todos los miembros de la clase que desea clonar sean tipos de valor o implementen ICloneable y que sus miembros sean tipos de valor o implementen ICloneable, etc.).
Para obtener una explicación más detallada sobre la clonación con ICloneable, consulte este artículo .
La respuesta larga es "depende". Como lo mencionaron otros, ICloneable no es compatible con los genéricos, requiere consideraciones especiales para las referencias de clase circulares y, de hecho, algunos lo ven como un "error" en .NET Framework. El método de serialización depende de que sus objetos sean serializables, lo cual puede no serlo y es posible que usted no tenga control. Todavía hay mucho debate en la comunidad sobre cuál es la "mejor" práctica. En realidad, ninguna de las soluciones es la mejor práctica de talla única para todas las situaciones, como se interpretó originalmente como ICloneable.
Consulte el artículo de este Rincón del desarrollador para ver algunas opciones más (crédito para Ian).
fuente
Salud.
fuente
EDITAR: el proyecto se suspende
Si desea una verdadera clonación a tipos desconocidos, puede echar un vistazo a fastclone .
Esa es la clonación basada en expresiones que funciona aproximadamente 10 veces más rápido que la serialización binaria y mantiene la integridad completa del gráfico de objetos.
Eso significa: si se refiere varias veces al mismo objeto en su jerarquía, el clon también tendrá una única instancia referenciada.
No hay necesidad de interfaces, atributos o cualquier otra modificación a los objetos que se están clonando.
fuente
Mantenga las cosas simples y use AutoMapper como otros mencionaron, es una pequeña biblioteca simple para mapear un objeto a otro ... Para copiar un objeto a otro con el mismo tipo, todo lo que necesita es tres líneas de código:
El objeto de destino ahora es una copia del objeto de origen. ¿No es lo suficientemente simple? Cree un método de extensión para usar en todas partes en su solución:
El método de extensión se puede usar de la siguiente manera:
fuente
Se me ocurrió esto para superar una deficiencia de .NET al tener que copiar manualmente en profundidad la Lista <T>.
Yo uso esto:
Y en otro lugar:
Traté de encontrar un oneliner que haga esto, pero no es posible, debido a que el rendimiento no funciona dentro de bloques de métodos anónimos.
Mejor aún, use el clonador genérico List <T>:
fuente
P. ¿Por qué elegiría esta respuesta?
En otras palabras, elija otra respuesta, a menos que tenga un cuello de botella en el rendimiento que deba corregirse, y pueda probarlo con un generador de perfiles .
10 veces más rápido que otros métodos
El siguiente método para realizar un clon profundo es:
Y el método ...
Para una velocidad máxima, puede usar Nested MemberwiseClone para hacer una copia profunda . Es casi la misma velocidad que copiar una estructura de valores, y es mucho más rápido que (a) la reflexión o (b) la serialización (como se describe en otras respuestas en esta página).
Tenga en cuenta que si usa Nested MemberwiseClone para una copia profunda , debe implementar manualmente una ShallowCopy para cada nivel anidado en la clase y una DeepCopy que llama a todos los métodos ShallowCopy para crear un clon completo. Esto es simple: solo unas pocas líneas en total, vea el código de demostración a continuación.
Aquí está la salida del código que muestra la diferencia de rendimiento relativa para 100,000 clones:
Usar Nested MemberwiseClone en una clase casi tan rápido como copiar una estructura, y copiar una estructura es bastante cercano a la velocidad máxima teórica que es capaz de hacer .NET.
Para comprender cómo hacer una copia profunda con MemberwiseCopy, aquí está el proyecto de demostración que se utilizó para generar los tiempos anteriores:
Luego, llame a la demostración desde main:
Nuevamente, tenga en cuenta que si usa Nested MemberwiseClone para una copia profunda , debe implementar manualmente una ShallowCopy para cada nivel anidado en la clase, y una DeepCopy que llama a todos los métodos ShallowCopy para crear un clon completo. Esto es simple: solo unas pocas líneas en total, consulte el código de demostración anterior.
Tipos de valor frente a tipos de referencias
Tenga en cuenta que cuando se trata de clonar un objeto, hay una gran diferencia entre una " estructura " y una " clase ":
Vea las diferencias entre los tipos de valores y los tipos de referencias .
Sumas de verificación para ayudar en la depuración
Realmente útil para desacoplar muchos hilos de muchos otros hilos
Un excelente caso de uso para este código es alimentar clones de una clase o estructura anidada en una cola, para implementar el patrón productor / consumidor.
ConcurrentQueue
.Esto funciona extremadamente bien en la práctica y nos permite desacoplar muchos hilos (los productores) de uno o más hilos (los consumidores).
Y este método también es increíblemente rápido: si usamos estructuras anidadas, es 35 veces más rápido que la serialización / deserialización de clases anidadas, y nos permite aprovechar todos los hilos disponibles en la máquina.
Actualizar
Aparentemente, ExpressMapper es tan rápido, si no más rápido, que la codificación manual como la anterior. Puede que tenga que ver cómo se comparan con un generador de perfiles.
fuente
En general, implementa la interfaz ICloneable e implementa Clone usted mismo. Los objetos C # tienen un método integrado MemberwiseClone que realiza una copia superficial que puede ayudarlo con todas las primitivas.
Para una copia profunda, no hay forma de saber cómo hacerlo automáticamente.
fuente
Lo he visto implementado a través de la reflexión también. Básicamente, había un método que iteraba a través de los miembros de un objeto y los copiaba adecuadamente al nuevo objeto. Cuando llegó a tipos de referencia o colecciones, creo que hizo una llamada recursiva sobre sí mismo. La reflexión es costosa, pero funcionó bastante bien.
fuente
Aquí hay una implementación de copia profunda:
fuente
Como no pude encontrar un clonador que cumpla con todos mis requisitos en diferentes proyectos, creé un clonador profundo que se puede configurar y adaptar a diferentes estructuras de código en lugar de adaptar mi código para cumplir con los requisitos de los clonadores. Se logra agregando anotaciones al código que se clonará o simplemente dejará el código como debe tener el comportamiento predeterminado. Utiliza la reflexión, el tipo de cachés y se basa en fasterflect . El proceso de clonación es muy rápido para una gran cantidad de datos y una alta jerarquía de objetos (en comparación con otros algoritmos basados en la reflexión / serialización).
https://github.com/kalisohn/CloneBehave
También disponible como paquete nuget: https://www.nuget.org/packages/Clone.Behave/1.0.0
Por ejemplo: el siguiente código hará DeepClone Address, pero solo realizará una copia superficial del campo _currentJob.
fuente
Generador de códigos
Hemos visto muchas ideas desde la serialización sobre la implementación manual hasta la reflexión y quiero proponer un enfoque totalmente diferente usando el generador de código CGbR . El método de generación de clones es eficiente en memoria y CPU y, por lo tanto, 300 veces más rápido que el DataContractSerializer estándar.
Todo lo que necesita es una definición de clase parcial con
ICloneable
y el generador hace el resto:Nota: La última versión tiene controles más nulos, pero los dejé fuera para una mejor comprensión.
fuente
Me gusta Copyconstructors así:
Si tienes más cosas para copiar, agrégalas
fuente
Este método resolvió el problema para mí:
Úselo así:
MyObj a = DeepCopy(b);
fuente
Aquí una solución rápida y fácil que funcionó para mí sin contar con Serialización / Deserialización.
EDITAR : requiere
Así es como lo usé
fuente
Sigue estos pasos:
ISelf<T>
con unaSelf
propiedad de solo lectura que devuelveT
, yICloneable<out T>
, que se deriva deISelf<T>
e incluye un métodoT Clone()
.CloneBase
tipo que implemente unaprotected virtual generic VirtualClone
conversiónMemberwiseClone
al tipo pasado.VirtualClone
llamando al método de clonación base y luego haciendo lo que sea necesario para clonar adecuadamente aquellos aspectos del tipo derivado que el método VirtualClone padre aún no ha manejado.Para una máxima versatilidad de herencia, las clases que exponen la funcionalidad de clonación pública deberían ser
sealed
, pero derivar de una clase base que de otro modo es idéntica, excepto por la falta de clonación. En lugar de pasar variables del tipo clonable explícito, tome un parámetro de tipoICloneable<theNonCloneableType>
. Esto permitirá una rutina que espera que una derivada clonable deFoo
funcione con una derivada clonable deDerivedFoo
, pero también permitirá la creación de derivados no clonables deFoo
.fuente
Creo que puedes probar esto.
fuente
He creado una versión de la respuesta aceptada que funciona con '[Serializable]' y '[DataContract]'. Ha pasado un tiempo desde que lo escribí, pero si no recuerdo mal, [DataContract] necesitaba un serializador diferente.
Requiere System, System.IO, System.Runtime.Serialization, System.Runtime.Serialization.Formatters.Binary, System.Xml ;
fuente
Ok, hay algunos ejemplos obvios con la reflexión en esta publicación, PERO la reflexión suele ser lenta, hasta que comienzas a almacenarla en caché correctamente.
si lo almacena en caché correctamente, clonará en profundidad el objeto 1000000 en 4,6s (medido por Watcher).
de lo que toma propiedades en caché o agrega nuevas al diccionario y las usa simplemente
verificación de código completo en mi publicación en otra respuesta
https://stackoverflow.com/a/34365709/4711853
fuente
prop.GetValue(...)
siguen siendo reflejo y no se pueden almacenar en caché. Sin embargo, en un árbol de expresión se compila, más rápidoComo casi todas las respuestas a esta pregunta han sido insatisfactorias o simplemente no funcionan en mi situación, escribí AnyClone, que se implementó completamente con reflexión y resolvió todas las necesidades aquí. No pude lograr que la serialización funcione en un escenario complicado con una estructura compleja, y no
IClonable
es lo ideal, de hecho, ni siquiera debería ser necesario.Estándar ignorar los atributos son compatibles usando
[IgnoreDataMember]
,[NonSerialized]
. Admite colecciones complejas, propiedades sin setters, campos de solo lectura, etc.Espero que ayude a alguien más que se encontró con los mismos problemas que yo.
fuente
Descargo de responsabilidad: soy el autor del paquete mencionado.
Me sorprendió cómo las principales respuestas a esta pregunta en 2019 todavía usan la serialización o la reflexión.
La serialización es limitante (requiere atributos, constructores específicos, etc.) y es muy lenta
BinaryFormatter
requiere elSerializable
atributo,JsonConverter
requiere un constructor o atributos sin parámetros, ninguno maneja muy bien los campos o interfaces de solo lectura y ambos son 10-30x más lentos de lo necesario.Árboles de expresión
En su lugar, puede usar Expression Trees o Reflection.Emit para generar código de clonación solo una vez, luego usar ese código compilado en lugar de reflexión lenta o serialización.
Habiendo encontrado el problema yo mismo y no encontré una solución satisfactoria, decidí crear un paquete que haga exactamente eso y funcione con cada tipo y sea casi tan rápido como el código escrito personalizado .
Puede encontrar el proyecto en GitHub: https://github.com/marcelltoth/ObjectCloner
Uso
Puede instalarlo desde NuGet. Obtenga el
ObjectCloner
paquete y úselo como:o si no le importa contaminar su tipo de objeto con extensiones, obtenga
ObjectCloner.Extensions
y escriba:Actuación
Un punto de referencia simple de clonar una jerarquía de clases mostró un rendimiento ~ 3 veces más rápido que usando Reflection, ~ 12 veces más rápido que la serialización de Newtonsoft.Json y ~ 36 veces más rápido que el altamente sugerido
BinaryFormatter
.fuente