c# - studio - Lista de clonación<T>
visual studio installer (8)
Dudo que tu ejemplo real tenga problemas, porque int
es un tipo de valor. Por ejemplo:
using System;
using System.Collections.Generic;
class Test
{
static void Main()
{
List<int> originalList = new List<int> { 5, 6, 7 };
List<int> cloneList = new List<int>(originalList);
cloneList.Add(8);
cloneList[0] = 2;
Console.WriteLine(originalList.Count); // Still 3
Console.WriteLine(originalList[0]); // Still 5
}
}
Sin embargo, como dice Marc, si su lista contiene tipos de referencia mutables, la clonación de la lista solo tomará una copia superficial : si cambia los objetos a los que hacen referencia las listas, esos cambios serán visibles a través de ambas listas. Sin embargo, el reemplazo de elementos en una lista no cambiará el elemento equivalente en la otra lista:
using System;
using System.Collections.Generic;
class Dummy
{
public int Value { get; set; }
public Dummy (int value)
{
this.Value = value;
}
}
class Test
{
static void Main()
{
List<Dummy> originalList = new List<Dummy>
{
new Dummy(5),
new Dummy(6),
new Dummy(7)
};
List<Dummy> cloneList = new List<Dummy>(originalList);
cloneList[0].Value = 1;
cloneList[1] = new Dummy(2);
Console.WriteLine(originalList[0].Value); // Changed to 1
Console.WriteLine(originalList[1].Value); // Still 6
}
}
Para tomar una "copia profunda" de una lista donde el tipo de elemento implemente ICloneable
, use:
List<Foo> cloneList = originalList.ConvertAll(x => (Foo) x.Clone());
Sin embargo, la profundidad real de este clon dependerá de la implementación de ICloneable
en el tipo de elemento: ICloneable
generalmente se considera como una cosa mala porque su contrato es tan vago.
Pensé que para clonar una Lista simplemente llamarías:
List<int> cloneList = new List<int>(originalList);
Pero probé eso en mi código y parece que estoy obteniendo efectos que implican que lo anterior simplemente está haciendo:
cloneList = originalList ... porque los cambios en cloneList parecen estar afectando a originalList.
Entonces, ¿cuál es la forma de clonar una lista?
EDITAR:
Estoy pensando en hacer algo como esto:
public static List<T> Clone<T>(this List<T> originalList) where T : ICloneable
{
return originalList.ConvertAll(x => (T) x.Clone());
}
EDIT2:
Tomé el código de copia profunda sugerido por Binoj Antony y creé este método de extensión:
public static T DeepCopy<T>(this T original) where T : class
{
using (MemoryStream memoryStream = new MemoryStream())
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(memoryStream, original);
memoryStream.Seek(0, SeekOrigin.Begin);
return (T)binaryFormatter.Deserialize(memoryStream);
}
}
EDIT3:
Ahora, diga que los elementos en la lista son estructuras. ¿Qué resultaría si llamara ?:
List<StructType> cloneList = new List<StructType>(originalList);
Estoy bastante seguro de que obtendría una lista llena de nuevos elementos únicos, ¿correcto?
Específicamente dice aquí que los artículos se copian a su nueva lista. Entonces sí, eso debería funcionar. Con los tipos de valor obtendrás completa independencia. Pero recuerde, con los tipos de referencia, las listas serán independientes pero estarán apuntando a los mismos objetos. Necesitarás copiar profundamente la lista.
Puede usar el siguiente código para hacer una copia profunda de la lista o de cualquier otro objeto que soporte la serialización:
También puede usar esto para cualquier versión de .NET framework desde v 2.0 y superior, y se puede aplicar una técnica similar (eliminando el uso de genéricos) y también se usa en 1.1.
public static class GenericCopier<T>
{
public static T DeepCopy(object objectToCopy)
{
using (MemoryStream memoryStream = new MemoryStream())
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(memoryStream, objectToCopy);
memoryStream.Seek(0, SeekOrigin.Begin);
return (T) binaryFormatter.Deserialize(memoryStream);
}
}
}
Puedes llamarlo usando
List<int> deepCopiedList = GenericCopier<List<int>>.DeepCopy(originalList);
Código completo para probar si esto funciona:
static void Main(string[] args)
{
List<int> originalList = new List<int>(5);
Random random = new Random();
for(int i = 0; i < 5; i++)
{
originalList.Add(random.Next(1, 100));
Console.WriteLine("List[{0}] = {1}", i, originalList[i]);
}
List<int> deepCopiedList = GenericCopier<List<int>>.DeepCopy(originalList);
for (int i = 0; i < 5; i++)
Console.WriteLine("deepCopiedList[{0}] value is {1}", i, deepCopiedList[i]);
}
List list = new List ();
List clone = new List (list);
list.Add (new int ());
Debug.Assert (list != clone);
Debug.Assert (list.Count == 1);
Debug.Assert (clone.Count == 0);
Este código funciona perfectamente según lo previsto para mí. ¿Estás cambiando los objetos EN la lista? Los elementos de la lista no serán clonados por la new List(oldList)
.
Debo agregar: si vas a utilizar la serialización para facilitar la copia profunda, ¿por qué clonarías cada elemento individual? Simplemente clone toda la lista original para comenzar.
A menos que tenga la lógica establecida de que solo clona nodos que cumplen ciertos criterios, hágalo nodo por nodo.
Usar el constructor List con la lista original como parámetro funcionará si el tipo subyacente de la lista es un tipo de valor . Para los elementos de lista de tipo de referencia, creo que desea copiarlos en profundidad .
Podrías hacer algo como esto:
(Suponiendo que el tipo subyacente implementa ICloneable)
originalList.ForEach((item) =>
{
cloneList.Add((ICloneable)item.Clone());
}
);
O usando algunos LINQ :
var cloneList = originalList.Select(item => (ICloneable)item.Clone()).ToList();
Yo voto para no confiar en la serialización de objetos. Es una práctica costosa y mala.
public static TObj CloneObject<TObj>(this TObj obj)
where TObj : ICloneable
{
return (TObj)obj.Clone();
}
El método anterior es mucho más elegante, y realmente debería importar implementar una interfaz clonable si la necesita. También podrías hacerlo genérico.
public interface ICloneable<T> : IClonable
{
T CloneObject();
}
Opcionalmente, puede abstenerse de utilizar la interfaz IClonable como tipo base, ya que está mal mantenido. El nombre del método tiene que cambiar porque no puede hacer sobrecargas en los tipos de devolución.
public static List<T> CloneList(this List<T> source)
where TObj : ICloneable
{
return source.Select(x=>x.CloneObject()).ToList();
}
Es tan simple como eso.
Quizás su problema se puede resolver usando tipos de valores en su lugar. Siempre son de pasada. Por lo tanto, nunca debe clonar nada siempre que su estructura de datos sea por valor.
Esto funcionaría ...
List<Foo> cloneList = new List<Foo>(originalList);
Cuando dice "porque los cambios en cloneList parecen estar afectando a originalList". - ¿Quiere decir cambios en la lista , o cambios en los elementos ...
Agregar / eliminar / intercambiar elementos está cambiando la lista , por lo que si lo hacemos:
cloneList.Add(anotherItem);
debería encontrar que cloneList
es más largo que originalList
. Sin embargo, si los contenidos son tipos de referencia (clases), ambas listas siguen apuntando a los mismos objetos subyacentes, por lo que si lo hacemos:
cloneList[0].SomeObjectProperty = 12345;
entonces esto también se mostrará contra originalList[0].SomeObjectProperty
- solo hay un único objeto (compartido entre ambas listas).
Si este es el problema, tendrás que clonar los objetos en la lista, y luego te enfrentarás a todo el problema profundo vs. superficial ... ¿este es el problema?
Para una copia superficial, es posible que pueda usar algo muy parecido a la respuesta aquí , simplemente con TTo = TFrom
(quizás simplifique a una única T
).