Tengo una lista genérica de objetos en C # y deseo clonar la lista. Los elementos dentro de la lista son clonables, pero no parece haber una opción para hacerlo list.Clone().
@orip ¿No es, clone()por definición, una copia profunda? En C # puedes pasar fácilmente los punteros con =, pensé.
Chris
13
@ Chris una copia superficial copia un nivel más profundo que la copia del puntero. Por ejemplo, una copia superficial de una lista tendrá los mismos elementos, pero será una lista diferente.
Creo que List.ConvertAll podría hacer esto en un tiempo más rápido, ya que puede preasignar toda la matriz para la lista, en lugar de tener que cambiar el tamaño todo el tiempo.
MichaelGG
2
@MichaelGG, ¿qué pasa si no desea convertir sino simplemente clonar / duplicar los elementos de la lista? ¿Funcionaría esto? || var clonedList = ListOfStrings.ConvertAll (p => p);
IbrarMumtaz
29
@IbrarMumtaz: Eso es lo mismo que var clonedList = new List <string> (ListOfStrings);
Brandon Arnold
44
Buena solución! Por cierto, prefiero la lista estática pública <T> CLone <T> ... Es más útil en casos como este, porque no se necesita más conversión: List <MyType> cloned = listToClone.Clone ();
Plutón
2
esto es una clonación profunda
George Birbilis
513
Si sus elementos son tipos de valor, puede hacer lo siguiente:
Sin embargo, si son tipos de referencia y desea una copia profunda (suponiendo que sus elementos se implementen correctamente ICloneable), podría hacer algo como esto:
Personalmente, lo evitaría ICloneablepor la necesidad de garantizar una copia profunda de todos los miembros. En cambio, sugeriría que el constructor de copias o un método de fábrica como YourType.CopyFrom(YourType itemToCopy)ese devuelva una nueva instancia deYourType .
Cualquiera de estas opciones podría ajustarse mediante un método (extensión u otro).
Creo que List <T> .ConvertAll podría verse mejor que crear una nueva lista y hacer un foreach + add.
MichaelGG
2
@Dimitri: No, eso no es cierto. El problema es que, cuando ICloneablese definió, la definición nunca indicó si el clon era profundo o poco profundo, por lo que no puede determinar qué tipo de operación Clone se realizará cuando un objeto lo implemente. Esto significa que si desea hacer un clon profundo List<T>, tendrá que hacerlo sin ICloneableasegurarse de que sea una copia profunda.
Jeff Yates
55
¿Por qué no usar el método AddRange? ( newList.AddRange(oldList.Select(i => i.Clone())o newList.AddRange(oldList.Select(i => new YourType(i))
phoog
55
@phoog: Creo que es un poco menos legible / comprensible al escanear el código, eso es todo. La legibilidad gana para mí.
Jeff Yates
1
@JeffYates: Una arruga insuficientemente considerada es que, en general, las cosas solo necesitan copiarse si existe alguna ruta de ejecución que las mute. Es muy común que los tipos inmutables contengan una referencia a una instancia de tipo mutable, pero nunca exponga esa instancia a nada que la mute. Copiar innecesariamente cosas que nunca van a cambiar a veces puede ser una pérdida de rendimiento importante , aumentando el uso de memoria por órdenes de magnitud.
supercat
84
Para una copia superficial, puede utilizar el método GetRange de la clase genérica List.
También puede lograr esto utilizando el contructor de List <T> para especificar una Lista <T> desde la cual copiar. por ejemplo, var shallowClonedList = new List <MyObject> (originalList);
Arkiliknam
99
Yo uso a menudo List<int> newList = oldList.ToList(). Mismo efecto. Sin embargo, la solución de Arkiliknam es la mejor para la legibilidad en mi opinión.
Dan Bechard el
82
publicstaticobjectDeepClone(object obj){object objResult =null;
using (MemoryStream ms =newMemoryStream()){BinaryFormatter bf =newBinaryFormatter();
bf.Serialize(ms, obj);
ms.Position=0;
objResult = bf.Deserialize(ms);}return objResult;}
Esta es una forma de hacerlo con C # y .NET 2.0. Tu objeto requiere ser [Serializable()]. El objetivo es perder todas las referencias y construir nuevas.
+1: me gusta esta respuesta: es rápida, sucia, desagradable y muy efectiva. Usé en Silverlight, y usé DataContractSerializer ya que BinarySerializer no estaba disponible. ¿Quién necesita escribir páginas de código de clonación de objetos cuando puede hacer esto? :)
slugster
3
Me gusta esto. Si bien es bueno hacer las cosas "bien", lo rápido y lo sucio suele ser útil.
Odrade
3
¡Rápido! pero: ¿por qué sucio?
Raiserle
2
Este clones profundos y es rápido y fácil. Cuidado con otras sugerencias en esta página. Intenté varios y no clonan profundamente.
Randall Hasta
2
El único aspecto negativo, si puede llamarlo así, es que sus clases deben marcarse Serializable para que esto funcione.
Tuukka Haapaniemi
30
Para clonar una lista simplemente llame a .ToList (). Esto crea una copia superficial.
Microsoft(R)Roslyn C# Compiler version 2.3.2.62116Loading context from'CSharpInteractive.rsp'.Type"#help"for more information.>var x =newList<int>(){3,4};>var y = x.ToList();> x.Add(5)> x
List<int>(3){3,4,5}> y
List<int>(2){3,4}>
Una pequeña advertencia de que esta es una copia superficial ... Esto creará dos objetos de lista, pero los objetos dentro serán los mismos. Es decir, cambiar una propiedad cambiará el mismo objeto / propiedad en la lista original.
Mark G el
22
Después de una ligera modificación, también puede clonar:
publicstatic T DeepClone<T>(T obj){
T objResult;
using (MemoryStream ms =newMemoryStream()){BinaryFormatter bf =newBinaryFormatter();
bf.Serialize(ms, obj);
ms.Position=0;
objResult =(T)bf.Deserialize(ms);}return objResult;}
No olvide que la T debe ser serializable, de lo contrario obtendrá System.Runtime.Serialization.SerializationException.
Bence Végert el
Buena respuesta. Una pista: podría agregar if (!obj.GetType().IsSerializable) return default(T);como la primera instrucción que evita la excepción. Y si lo cambia a un método de extensión, incluso podría usar el operador Elvis como var b = a?.DeepClone();(dado, var a = new List<string>() { "a", "b" }; por ejemplo).
Matt
15
A menos que necesite un clon real de cada objeto dentro de su List<T>, la mejor manera de clonar una lista es crear una nueva lista con la lista anterior como parámetro de colección.
Posiblemente no sea la forma más eficiente de hacerlo, pero a menos que lo esté haciendo cientos de miles de veces, es posible que ni siquiera note la diferencia de velocidad.
No se trata de la diferencia de velocidad, se trata de la legibilidad. Si llego a esta línea de código, me golpearía la cabeza y me preguntaría por qué introdujeron una biblioteca de terceros para serializar y luego deserializar un objeto que no tendría idea de por qué está sucediendo. Además, esto no funcionaría para una lista de modelos con objetos que tienen una estructura circular.
Jonathon Cwik
1
Este código funcionó excelentemente para mí para la clonación profunda. La aplicación está migrando la plantilla de documentos de Dev a QA a Prod. Cada objeto es un paquete de varios objetos de plantilla de documento, y cada documento a su vez se compone de una lista de objetos de párrafo. Este código me permite serializar los objetos "fuente" .NET e inmediatamente deserializarlos a nuevos objetos "destino", que luego se guardan en una base de datos SQL en un entorno diferente. Después de toneladas de investigación, encontré muchas cosas, muchas de las cuales eran demasiado engorrosas, y decidí probar esto. ¡Este enfoque corto y flexible fue "justo"!
Mi amigo Gregor Martinovic y yo se nos ocurrió esta solución fácil usando un serializador de JavaScript. No es necesario marcar las clases como serializables y en nuestras pruebas con Newtonsoft JsonSerializer incluso más rápido que con BinaryFormatter. Con métodos de extensión utilizables en cada objeto.
Opción estándar .NET JavascriptSerializer:
publicstatic T DeepCopy<T>(this T value){JavaScriptSerializer js =newJavaScriptSerializer();string json = js.Serialize(value);return js.Deserialize<T>(json);}
Tendré suerte si alguien lee esto ... pero para no devolver una lista de objetos tipo en mis métodos Clone, creé una interfaz:
publicinterfaceIMyCloneable<T>{
T Clone();}
Luego especifiqué la extensión:
publicstaticList<T>Clone<T>(thisList<T> listToClone)where T :IMyCloneable<T>{return listToClone.Select(item =>(T)item.Clone()).ToList();}
Y aquí hay una implementación de la interfaz en mi software de marcado A / V. Quería que mi método Clone () devolviera una lista de VidMark (mientras que la interfaz ICloneable quería que mi método devolviera una lista de objetos):
Y finalmente, el uso de la extensión dentro de una clase:
privateList<VidMark>_VidMarks;privateList<VidMark>_UndoVidMarks;//Other methods instantiate and fill the listsprivatevoidSetUndoVidMarks(){_UndoVidMarks=_VidMarks.Clone();}
También puede simplemente convertir la lista a una matriz usando ToArray, y luego clonar la matriz usando Array.Clone(...). Dependiendo de sus necesidades, los métodos incluidos en la clase Array podrían satisfacer sus necesidades.
Esto no funciona; cambios en los valores en la matriz clonada TODAVÍA cambian los valores en la lista original.
Bernoulli Lizard
puede usar var clonedList = ListOfStrings.ConvertAll (p => p); según lo dado por @IbrarMumtaz ... Funciona de manera efectiva ... Los cambios en una lista se guardan en sí mismos y no se reflejan en otra
zainul
2
Puedes usar el método de extensión:
namespace extension
{publicclass ext
{publicstaticList<double> clone(thisList<double> t){List<double> kop =newList<double>();int x;for(x =0; x < t.Count; x++){
kop.Add(t[x]);}return kop;}};}
Puede clonar todos los objetos utilizando sus miembros de tipo de valor, por ejemplo, considere esta clase:
publicclass matrix
{publicList<List<double>> mat;publicint rows,cols;public matrix clone(){// create new object
matrix copy =new matrix();// firstly I can directly copy rows and cols because they are value types
copy.rows =this.rows;
copy.cols =this.cols;// but now I can no t directly copy mat because it is not value type soint x;// I assume I have clone method for List<double>for(x=0;x<this.mat.count;x++){
copy.mat.Add(this.mat[x].clone());}// then mat is clonedreturn copy;// and copy of original is returned }};
Nota: si realiza algún cambio en la copia (o clonación), no afectará al objeto original.
Parece que algunas colecciones (p. ej., SelectedItems de DataGrid en Silverlight) omiten la implementación de CopyTo, lo cual es un problema con este enfoque
George Birbilis
1
Yo uso automapper para copiar un objeto. Acabo de configurar una asignación que asigna un objeto a sí mismo. Puede ajustar esta operación de la forma que desee.
Para una copia profunda, ICloneable es la solución correcta, pero aquí hay un enfoque similar a ICloneable utilizando el constructor en lugar de la interfaz ICloneable.
publicclassStudent{publicStudent(Student student){FirstName= student.FirstName;LastName= student.LastName;}publicstringFirstName{get;set;}publicstringLastName{get;set;}}// wherever you have the listList<Student> students;// and then where you want to make a copyList<Student> copy = students.Select(s =>newStudent(s)).ToList();
necesitará la siguiente biblioteca donde hace la copia
using System.Linq
También puede usar un bucle for en lugar de System.Linq, pero Linq lo hace conciso y limpio. Del mismo modo, podría hacer lo que otras respuestas han sugerido y hacer métodos de extensión, etc., pero nada de eso es necesario.
Eso se llama un "constructor de copias". Es un enfoque propenso a errores, cada vez que agrega un nuevo campo a Estudiante, debe recordar agregarlo al constructor de la copia. La idea principal detrás de "clonar" es evitar ese problema.
kenno
2
Incluso con ICloneable, debe tener un método "Clone" en su clase. A menos que use la reflexión (que también podría usar en el enfoque anterior), ese método Clone se verá muy similar al enfoque del constructor de copias anterior y sufrirá el mismo problema de tener que actualizar los campos nuevos / modificados. Pero eso dice "La clase tiene que actualizarse cuando cambian los campos de la clase". Por supuesto que sí;)
ztorstri
0
El siguiente código debe transferirse a una lista con cambios mínimos.
Básicamente funciona insertando un nuevo número aleatorio de un rango mayor con cada ciclo sucesivo. Si ya existen números que son iguales o superiores, cambie esos números aleatorios hacia arriba para que se transfieran al nuevo rango más grande de índices aleatorios.
// Example Usageint[] indexes = getRandomUniqueIndexArray(selectFrom.Length, toSet.Length);for(int i =0; i < toSet.Length; i++)
toSet[i]= selectFrom[indexes[i]];privateint[] getRandomUniqueIndexArray(int length,int count){if(count > length || count <1|| length <1)returnnewint[0];int[] toReturn =newint[count];if(count == length){for(int i =0; i < toReturn.Length; i++) toReturn[i]= i;return toReturn;}Random r =newRandom();int startPos = count -1;for(int i = startPos; i >=0; i--){int index = r.Next(length - i);for(int j = startPos; j > i; j--)if(toReturn[j]>= index)
toReturn[j]++;
toReturn[i]= index;}return toReturn;}
Otra cosa: podrías usar la reflexión. Si almacena esto correctamente, clonará 1,000,000 de objetos en 5.6 segundos (lamentablemente, 16.4 segundos con objetos internos).
[ProtoContract(ImplicitFields=ImplicitFields.AllPublic)]publicclassPerson{...JobJobDescription...}[ProtoContract(ImplicitFields=ImplicitFields.AllPublic)]publicclassJob{...}privatestaticreadonlyType stringType =typeof(string);publicstaticclassCopyFactory{staticreadonlyDictionary<Type,PropertyInfo[]>ProperyList=newDictionary<Type,PropertyInfo[]>();privatestaticreadonlyMethodInfoCreateCopyReflectionMethod;staticCopyFactory(){CreateCopyReflectionMethod=typeof(CopyFactory).GetMethod("CreateCopyReflection",BindingFlags.Static|BindingFlags.Public);}publicstatic T CreateCopyReflection<T>(T source)where T :new(){var copyInstance =new T();var sourceType =typeof(T);PropertyInfo[] propList;if(ProperyList.ContainsKey(sourceType))
propList =ProperyList[sourceType];else{
propList = sourceType.GetProperties(BindingFlags.Public|BindingFlags.Instance);ProperyList.Add(sourceType, propList);}foreach(var prop in propList){varvalue= prop.GetValue(source,null);
prop.SetValue(copyInstance,value!=null&& prop.PropertyType.IsClass&& prop.PropertyType!= stringType ?CreateCopyReflectionMethod.MakeGenericMethod(prop.PropertyType).Invoke(null,newobject[]{value}):value,null);}return copyInstance;}
Lo medí de una manera simple, usando la clase Watcher.
var person =newPerson{...};for(var i =0; i <1000000; i++){
personList.Add(person);}var watcher =newStopwatch();
watcher.Start();var copylist = personList.Select(CopyFactory.CreateCopyReflection).ToList();
watcher.Stop();var elapsed = watcher.Elapsed;
CopyFactory es solo mi clase de prueba donde tengo una docena de pruebas que incluyen el uso de la expresión. Puede implementar esto de otra forma en una extensión o lo que sea. No te olvides del almacenamiento en caché.
Todavía no probé la serialización, pero dudo de una mejora con un millón de clases. Probaré algo rápido protobuf / newton.
PD: en aras de la simplicidad de lectura, solo utilicé la propiedad automática aquí. Podría actualizar con FieldInfo, o debería implementar esto fácilmente por su cuenta.
Recientemente probé el serializador Protocol Buffers con la función DeepClone lista para usar. Gana con 4.2 segundos en un millón de objetos simples, pero cuando se trata de objetos internos, gana con el resultado 7.4 segundos.
Serializer.DeepClone(personList);
RESUMEN: Si no tienes acceso a las clases, esto te ayudará. De lo contrario, depende del recuento de los objetos. Creo que podría usar la reflexión de hasta 10,000 objetos (quizás un poco menos), pero para más de esto el serializador de Protocol Buffers funcionará mejor.
Hay una manera simple de clonar objetos en C # usando un serializador y deserializador JSON.
Puede crear una clase de extensión:
using Newtonsoft.Json;staticclass typeExtensions
{[Extension()]publicstatic T jsonCloneObject<T>(T source){string json =JsonConvert.SerializeObject(source);returnJsonConvert.DeserializeObject<T>(json);}}
clone()
por definición, una copia profunda? En C # puedes pasar fácilmente los punteros con =, pensé.Respuestas:
Puedes usar un método de extensión.
fuente
Si sus elementos son tipos de valor, puede hacer lo siguiente:
Sin embargo, si son tipos de referencia y desea una copia profunda (suponiendo que sus elementos se implementen correctamente
ICloneable
), podría hacer algo como esto:Obviamente, reemplace
ICloneable
en los genéricos anteriores y eche cualquier tipo de elemento que implementeICloneable
.Si su tipo de elemento no es compatible
ICloneable
pero tiene un constructor de copia, puede hacer esto en su lugar:Personalmente, lo evitaría
ICloneable
por la necesidad de garantizar una copia profunda de todos los miembros. En cambio, sugeriría que el constructor de copias o un método de fábrica comoYourType.CopyFrom(YourType itemToCopy)
ese devuelva una nueva instancia deYourType
.Cualquiera de estas opciones podría ajustarse mediante un método (extensión u otro).
fuente
ICloneable
se definió, la definición nunca indicó si el clon era profundo o poco profundo, por lo que no puede determinar qué tipo de operación Clone se realizará cuando un objeto lo implemente. Esto significa que si desea hacer un clon profundoList<T>
, tendrá que hacerlo sinICloneable
asegurarse de que sea una copia profunda.newList.AddRange(oldList.Select(i => i.Clone())
onewList.AddRange(oldList.Select(i => new YourType(i)
)Para una copia superficial, puede utilizar el método GetRange de la clase genérica List.
Citado de: Recetas genéricas
fuente
List<int> newList = oldList.ToList()
. Mismo efecto. Sin embargo, la solución de Arkiliknam es la mejor para la legibilidad en mi opinión.Esta es una forma de hacerlo con C # y .NET 2.0. Tu objeto requiere ser
[Serializable()]
. El objetivo es perder todas las referencias y construir nuevas.fuente
Para clonar una lista simplemente llame a .ToList (). Esto crea una copia superficial.
fuente
Después de una ligera modificación, también puede clonar:
fuente
if (!obj.GetType().IsSerializable) return default(T);
como la primera instrucción que evita la excepción. Y si lo cambia a un método de extensión, incluso podría usar el operador Elvis comovar b = a?.DeepClone();
(dado,var a = new List<string>() { "a", "b" };
por ejemplo).A menos que necesite un clon real de cada objeto dentro de su
List<T>
, la mejor manera de clonar una lista es crear una nueva lista con la lista anterior como parámetro de colección.Los cambios
myList
como insertar o eliminar no afectaráncloneOfMyList
y viceversa.Sin embargo, los objetos reales que contienen las dos listas siguen siendo los mismos.
fuente
Usar AutoMapper (o cualquier lib de mapeo que prefiera) para clonar es simple y muy fácil de mantener.
Defina su mapeo:
Haz la magia
fuente
Si solo te interesan los tipos de valor ...
Y sabes el tipo:
Si no conoce el tipo antes, necesitará una función auxiliar:
El justo:
fuente
Si ya ha hecho referencia a Newtonsoft.Json en su proyecto y sus objetos son serializables, siempre puede usar:
Posiblemente no sea la forma más eficiente de hacerlo, pero a menos que lo esté haciendo cientos de miles de veces, es posible que ni siquiera note la diferencia de velocidad.
fuente
fuente
fuente
fuente
Mi amigo Gregor Martinovic y yo se nos ocurrió esta solución fácil usando un serializador de JavaScript. No es necesario marcar las clases como serializables y en nuestras pruebas con Newtonsoft JsonSerializer incluso más rápido que con BinaryFormatter. Con métodos de extensión utilizables en cada objeto.
Opción estándar .NET JavascriptSerializer:
Opción más rápida con Newtonsoft JSON :
fuente
fuente
Tendré suerte si alguien lee esto ... pero para no devolver una lista de objetos tipo en mis métodos Clone, creé una interfaz:
Luego especifiqué la extensión:
Y aquí hay una implementación de la interfaz en mi software de marcado A / V. Quería que mi método Clone () devolviera una lista de VidMark (mientras que la interfaz ICloneable quería que mi método devolviera una lista de objetos):
Y finalmente, el uso de la extensión dentro de una clase:
A alguien le gusta? ¿Alguna mejora?
fuente
También puede simplemente convertir la lista a una matriz usando
ToArray
, y luego clonar la matriz usandoArray.Clone(...)
. Dependiendo de sus necesidades, los métodos incluidos en la clase Array podrían satisfacer sus necesidades.fuente
Puedes usar el método de extensión:
Puede clonar todos los objetos utilizando sus miembros de tipo de valor, por ejemplo, considere esta clase:
Nota: si realiza algún cambio en la copia (o clonación), no afectará al objeto original.
fuente
Si necesita una lista clonada con la misma capacidad, puede probar esto:
fuente
He creado una extensión que convierte ICollection de elementos que no implementan IClonable
fuente
Yo uso automapper para copiar un objeto. Acabo de configurar una asignación que asigna un objeto a sí mismo. Puede ajustar esta operación de la forma que desee.
http://automapper.codeplex.com/
fuente
Usar un yeso puede ser útil, en este caso, para una copia superficial:
aplicado a la lista genérica:
fuente
Para una copia profunda, ICloneable es la solución correcta, pero aquí hay un enfoque similar a ICloneable utilizando el constructor en lugar de la interfaz ICloneable.
necesitará la siguiente biblioteca donde hace la copia
También puede usar un bucle for en lugar de System.Linq, pero Linq lo hace conciso y limpio. Del mismo modo, podría hacer lo que otras respuestas han sugerido y hacer métodos de extensión, etc., pero nada de eso es necesario.
fuente
El siguiente código debe transferirse a una lista con cambios mínimos.
Básicamente funciona insertando un nuevo número aleatorio de un rango mayor con cada ciclo sucesivo. Si ya existen números que son iguales o superiores, cambie esos números aleatorios hacia arriba para que se transfieran al nuevo rango más grande de índices aleatorios.
fuente
Otra cosa: podrías usar la reflexión. Si almacena esto correctamente, clonará 1,000,000 de objetos en 5.6 segundos (lamentablemente, 16.4 segundos con objetos internos).
Lo medí de una manera simple, usando la clase Watcher.
RESULTADO: Con objeto interno PersonInstance - 16.4, PersonInstance = null - 5.6
CopyFactory es solo mi clase de prueba donde tengo una docena de pruebas que incluyen el uso de la expresión. Puede implementar esto de otra forma en una extensión o lo que sea. No te olvides del almacenamiento en caché.
Todavía no probé la serialización, pero dudo de una mejora con un millón de clases. Probaré algo rápido protobuf / newton.
PD: en aras de la simplicidad de lectura, solo utilicé la propiedad automática aquí. Podría actualizar con FieldInfo, o debería implementar esto fácilmente por su cuenta.
Recientemente probé el serializador Protocol Buffers con la función DeepClone lista para usar. Gana con 4.2 segundos en un millón de objetos simples, pero cuando se trata de objetos internos, gana con el resultado 7.4 segundos.
RESUMEN: Si no tienes acceso a las clases, esto te ayudará. De lo contrario, depende del recuento de los objetos. Creo que podría usar la reflexión de hasta 10,000 objetos (quizás un poco menos), pero para más de esto el serializador de Protocol Buffers funcionará mejor.
fuente
Hay una manera simple de clonar objetos en C # usando un serializador y deserializador JSON.
Puede crear una clase de extensión:
Para clonar y objetar:
fuente