generics clone - ¿Cómo puedo clonar una lista genérica en C#?




without reference (20)

Tengo una lista genérica de objetos en C #, y deseo clonar la lista. Los elementos de la lista se pueden clonar, pero no parece haber una opción para hacer list.Clone() .

¿Hay una manera fácil de evitar esto?


Answers

Otra cosa: podrías usar la reflexión. Si guardará este caché correctamente, clonará 1,000,000 de objetos en 5.6 segundos (lamentablemente, 16.4 segundos con objetos internos).

[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
public class Person
{
       ...
      Job JobDescription
       ...
}

[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
public class Job
{...
}

private static readonly Type stringType = typeof (string);

public static class CopyFactory
{
    static readonly Dictionary<Type, PropertyInfo[]> ProperyList = new Dictionary<Type, PropertyInfo[]>();

    private static readonly MethodInfo CreateCopyReflectionMethod;

    static CopyFactory()
    {
        CreateCopyReflectionMethod = typeof(CopyFactory).GetMethod("CreateCopyReflection", BindingFlags.Static | BindingFlags.Public);
    }

    public static 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)
        {
            var value = prop.GetValue(source, null);
            prop.SetValue(copyInstance,
                value != null && prop.PropertyType.IsClass && prop.PropertyType != stringType ? CreateCopyReflectionMethod.MakeGenericMethod(prop.PropertyType).Invoke(null, new object[] { value }) : value, null);
        }

        return copyInstance;
    }

Lo medí de una manera simple, usando la clase Watcher.

 var person = new Person
 {
     ...
 };

 for (var i = 0; i < 1000000; i++)
 {
    personList.Add(person);
 }
 var watcher = new Stopwatch();
 watcher.Start();
 var copylist = personList.Select(CopyFactory.CreateCopyReflection).ToList();
 watcher.Stop();
 var elapsed = watcher.Elapsed;

RESULTADO: Con el 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 expresiones. Podría implementar esto en otra forma en una extensión o lo que sea. No te olvides del almacenamiento en caché.

No probé la serialización todavía, pero dudo en una mejora con un millón de clases. Voy a intentar algo rápido protobuf / newton.

PD: en aras de la simplicidad de lectura, solo usé auto-propiedad aquí. Podría actualizar con FieldInfo, o debería implementarlo fácilmente por su cuenta.

Recientemente probé el serializador Protocol Buffers con la función DeepClone fuera de la caja. Gana con 4.2 segundos en un millón de objetos simples, pero cuando se trata de objetos internos, gana con el resultado de 7.4 segundos.

Serializer.DeepClone(personList);

RESUMEN: Si no tiene acceso a las clases, esto ayudará. De lo contrario, depende de la cuenta de los objetos. Creo que podrías usar la reflexión de hasta 10,000 objetos (quizás un poco menos), pero por más que esto, el serializador de Protocol Buffers tendrá un mejor desempeño.


Yo uso automapper para copiar un objeto. Acabo de configurar una asignación que asigna un objeto a sí mismo. Puedes envolver esta operación como quieras.

http://automapper.codeplex.com/


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 más altos, cambie esos números aleatorios hacia arriba para que se transfieran al nuevo rango más grande de índices aleatorios.

// Example Usage
int[] indexes = getRandomUniqueIndexArray(selectFrom.Length, toSet.Length);

for(int i = 0; i < toSet.Length; i++)
    toSet[i] = selectFrom[indexes[i]];


private int[] getRandomUniqueIndexArray(int length, int count)
{
    if(count > length || count < 1 || length < 1)
        return new int[0];

    int[] toReturn = new int[count];
    if(count == length)
    {
        for(int i = 0; i < toReturn.Length; i++) toReturn[i] = i;
        return toReturn;
    }

    Random r = new Random();
    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;
}

Si sus elementos son tipos de valor, entonces simplemente puede hacer:

List<YourType> newList = new List<YourType>(oldList);

Sin embargo, si son tipos de referencia y desea una copia profunda (suponiendo que sus elementos implementan ICloneable correctamente), podría hacer algo como esto:

List<ICloneable> oldList = new List<ICloneable>();
List<ICloneable> newList = new List<ICloneable>(oldList.Count);

oldList.ForEach((item) =>
    {
        newList.Add((ICloneable)item.Clone());
    });

Obviamente, reemplace ICloneable en los genéricos anteriores y emita cualquier tipo de elemento que implemente ICloneable .

Si su tipo de elemento no admite ICloneable pero tiene un constructor de copia, podría hacer esto en su lugar:

List<YourType> oldList = new List<YourType>();
List<YourType> newList = new List<YourType>(oldList.Count);

oldList.ForEach((item)=>
    {
        newList.Add(new YourType(item));
    });

Personalmente, evitaría ICloneable debido a la necesidad de garantizar una copia profunda de todos los miembros. En su lugar, sugeriría el constructor de copia o un método de fábrica como YourType.CopyFrom(YourType itemToCopy) que devuelve una nueva instancia de YourType .

Cualquiera de estas opciones podría ser envuelta por un método (extensión o de otro tipo).


Puede utilizar un método de extensión.

static class Extensions
{
    public static IList<T> Clone<T>(this IList<T> listToClone) where T: ICloneable
    {
        return listToClone.Select(item => (T)item.Clone()).ToList();
    }
}

 //try this
 List<string> ListCopy= new List<string>(OldList);
 //or try
 List<T> ListCopy=OldList.ToList();

He creado para mi una extensión que convierte ICollection de elementos que no implementan IClonable

static class CollectionExtensions
{
    public static ICollection<T> Clone<T>(this ICollection<T> listToClone)
    {
        var array = new T[listToClone.Count];
        listToClone.CopyTo(array,0);
        return array.ToList();
    }
}

Hay una forma sencilla de clonar objetos en C # utilizando un serializador JSON y deserializador.

Puedes crear una clase de extensión:

using Newtonsoft.Json;

static class typeExtensions
{
    [Extension()]
    public static T jsonCloneObject<T>(T source)
    {
    string json = JsonConvert.SerializeObject(source);
    return JsonConvert.DeserializeObject<T>(json);
    }
}

Para clonar y objetar:

obj clonedObj = originalObj.jsonCloneObject;

Si necesita una lista clonada con la misma capacidad, puede intentar esto:

public static List<T> Clone<T>(this List<T> oldList)
{
    var newList = new List<T>(oldList.Capacity);
    newList.AddRange(oldList);
    return newList;
}

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.


Mi amigo Gregor Martinovic y yo creamos esta solución fácil utilizando 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:

public static T DeepCopy<T>(this T value)
{
    JavaScriptSerializer js = new JavaScriptSerializer();

    string json = js.Serialize(value);

    return js.Deserialize<T>(json);
}

Opción más rápida utilizando Newtonsoft JSON :

public static T DeepCopy<T>(this T value)
{
    string json = JsonConvert.SerializeObject(value);

    return JsonConvert.DeserializeObject<T>(json);
}

Para una copia superficial, puede utilizar el método GetRange de la clase de lista genérica.

List<int> oldList = new List<int>( );
// Populate oldList...

List<int> newList = oldList.GetRange(0, oldList.Count);

Citado de: Recetas de genéricos


Usar el AutoMapper (o la biblioteca de mapeo que prefieras) para clonar es simple y mucho más fácil de mantener.

Defina su mapeo:

Mapper.CreateMap<YourType, YourType>();

Hacer la magia

YourTypeList.ConvertAll(Mapper.Map<YourType, YourType>);

Después de una ligera modificación también puedes clonar:

public static T DeepClone<T>(T obj)
{
    T objResult;
    using (MemoryStream ms = new MemoryStream())
    {
        BinaryFormatter bf = new BinaryFormatter();
        bf.Serialize(ms, obj);
        ms.Position = 0;
        objResult = (T)bf.Deserialize(ms);
    }
    return objResult;
}

Si ya ha hecho referencia a Newtonsoft.Json en su proyecto y sus objetos se pueden serializar, siempre puede usar:

List<T> newList = JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(listToCopy))

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.


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 antigua como parámetro de colección.

List<T> myList = ...;
List<T> cloneOfMyList = new List<T>(myList);

Los cambios en myList , como insertar o eliminar, no afectarán a cloneOfMyList y viceversa.

Sin embargo, los objetos reales que contienen las dos listas siguen siendo los mismos.


Puede utilizar el método de extensión:

namespace extension
{
    public class ext
    {
        public static List<double> clone(this List<double> t)
        {
            List<double> kop = new List<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:

public class matrix
{
    public List<List<double>> mat;
    public int 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 so
        int 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 cloned
        return copy; // and copy of original is returned 
    }
};

Nota: si realiza algún cambio en la copia (o clonación) no afectará al objeto original.


Para clonar una lista solo llame a .ToList ()

Microsoft (R) Roslyn C# Compiler version 2.3.2.62116
Loading context from 'CSharpInteractive.rsp'.
Type "#help" for more information.
> var x = new List<int>() { 3, 4 };
> var y = x.ToList();
> x.Add(5)
> x
List<int>(3) { 3, 4, 5 }
> y
List<int>(2) { 3, 4 }
> 

public static Object CloneType(Object objtype)
{
    Object lstfinal = new Object();

    using (MemoryStream memStream = new MemoryStream())
    {
        BinaryFormatter binaryFormatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone));
        binaryFormatter.Serialize(memStream, objtype); memStream.Seek(0, SeekOrigin.Begin);
        lstfinal = binaryFormatter.Deserialize(memStream);
    }

    return lstfinal;
}

Contra lo que parece ser una práctica común entre otros programadores, prefiero String sobre string , solo para resaltar el hecho de que String es un tipo de referencia, como mencionó Jon Skeet.





c# generics list clone