Comment cloner une liste générique en C #?

J’ai une liste générique d’objects en C # et je souhaite cloner la liste. Les éléments de la liste sont clonables, mais il ne semble pas y avoir d’option pour list.Clone() .

Y a-t-il un moyen facile de contourner cela?

Vous pouvez utiliser une méthode d’extension.

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

Si vos éléments sont des types de valeur, vous pouvez simplement faire:

 List newList = new List(oldList); 

Cependant, si ce sont des types de référence et que vous voulez une copie profonde (en supposant que vos éléments implémentent correctement ICloneable ), vous pouvez faire quelque chose comme ceci:

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

Évidemment, remplacez ICloneable dans les génériques ci-dessus et convertissez-le avec ce que votre type d’élément implémente ICloneable .

Si votre type d’élément ne prend pas en charge ICloneable mais possède un constructeur de copie, vous pouvez le faire à la place:

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

Personnellement, ICloneable raison de la nécessité de garantir une copie complète de tous les membres. Au lieu de cela, je suggère le constructeur de copie ou une méthode d’usine comme YourType.CopyFrom(YourType itemToCopy) qui renvoie une nouvelle instance de YourType .

Chacune de ces options pourrait être enveloppée par une méthode (extension ou autre).

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

C’est une façon de le faire avec C # et .NET 2.0. Votre object doit être [Serializable()] . L’objective est de perdre toutes les références et d’en construire de nouvelles.

Pour une copie superficielle, vous pouvez utiliser la méthode GetRange de la classe List générique.

 List oldList = new List( ); // Populate oldList... List newList = oldList.GetRange(0, oldList.Count); 

Cité de: Recettes génériques

Après une légère modification, vous pouvez également cloner:

 public static T DeepClone(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; } 

Utiliser AutoMapper (ou tout autre logiciel de cartographie que vous préférez) pour cloner est simple et beaucoup plus facile à maintenir.

Définissez votre mapping:

 Mapper.CreateMap(); 

Faites la magie:

 YourTypeList.ConvertAll(Mapper.Map); 

Si vous ne vous souciez que des types de valeur …

Et vous connaissez le type:

 List newList = new List(oldList); 

Si vous ne connaissez pas le type avant, vous aurez besoin d’une fonction d’assistance:

 List Clone(IEnumerable oldList) { return newList = new List(oldList); } 

Le juste:

 List myNewList = Clone(myOldList); 

Sauf si vous avez besoin d’un clone réel de chaque object de votre List , la meilleure façon de cloner une liste est de créer une nouvelle liste avec l’ancienne liste en tant que paramètre de collection.

 List myList = ...; List cloneOfMyList = new List(myList); 

Les modifications apscopes à myList telles que insert ou remove, n’affecteront pas cloneOfMyList et vice versa.

Les objects réels que contiennent les deux listes sont cependant toujours les mêmes.

Pour cloner une liste, appelez simplement .ToList ()

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

Si vous avez déjà référencé Newtonsoft.Json dans votre projet et que vos objects sont sérialisables, vous pouvez toujours utiliser:

 List newList = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(listToCopy)) 

Peut-être pas le moyen le plus efficace de le faire, mais à moins que vous ne le fassiez des centaines de milliers de fois, vous ne remarquerez peut-être même pas la différence de vitesse.

 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; } 
 public class CloneableList : List, ICloneable where T : ICloneable { public object Clone() { var clone = new List(); ForEach(item => clone.Add((T)item.Clone())); return clone; } } 
  public List Clone(List o1List) where TEntity : class , new() { List retList = new List(); try { Type sourceType = typeof(TEntity); foreach(var o1 in o1List) { TEntity o2 = new TEntity(); foreach (PropertyInfo propInfo in (sourceType.GetProperties())) { var val = propInfo.GetValue(o1, null); propInfo.SetValue(o2, val); } retList.Add(o2); } return retList; } catch { return retList; } } 

Vous pouvez également simplement convertir la liste en tableau à l’aide de ToArray , puis cloner le tableau à l’aide de Array.Clone(...) . Selon vos besoins, les méthodes incluses dans la classe Array peuvent répondre à vos besoins.

Vous pouvez utiliser la méthode d’extension:

 namespace extension { public class ext { public static List clone(this List t) { List kop = new List(); int x; for (x = 0; x < t.Count; x++) { kop.Add(t[x]); } return kop; } }; } 

Vous pouvez cloner tous les objects en utilisant leurs membres de type valeur, par exemple, considérez cette classe:

 public class masortingx { public List> mat; public int rows,cols; public masortingx clone() { // create new object masortingx copy = new masortingx(); // 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 for(x=0;x 

Remarque: si vous effectuez des modifications sur copy (ou clone), cela n'affectera pas l'object d'origine.

Si vous avez besoin d’une liste clonée avec la même capacité, vous pouvez essayer ceci:

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

Mon ami Gregor Martinovic et moi avons conçu cette solution simple en utilisant un sérialiseur JavaScript. Il n’est pas nécessaire de marquer les classes comme Serializable et, dans nos tests, d’utiliser Newtonsoft JsonSerializer encore plus rapidement qu’avec BinaryFormatter. Avec des méthodes d’extension utilisables sur chaque object.

Option standard .NET JavascriptSerializer:

 public static T DeepCopy(this T value) { JavaScriptSerializer js = new JavaScriptSerializer(); ssortingng json = js.Serialize(value); return js.Deserialize(json); } 

Option plus rapide utilisant Newtonsoft JSON :

 public static T DeepCopy(this T value) { ssortingng json = JsonConvert.SerializeObject(value); return JsonConvert.DeserializeObject(json); } 

J’ai fait pour moi une extension qui convertit ICollection des éléments qui n’implémentent pas IClonable

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

J’utilise automapper pour copier un object. Je viens de configurer un mappage qui mappe un object sur lui-même. Vous pouvez envelopper cette opération comme vous le souhaitez.

http://automapper.codeplex.com/

Le code suivant doit être transféré sur une liste avec des modifications minimales.

Fondamentalement, cela fonctionne en insérant un nouveau nombre aléatoire à partir d’une plus grande plage avec chaque boucle successive. S’il existe déjà des nombres identiques ou supérieurs, déplacez ces nombres aléatoires vers le haut afin de les transférer dans la nouvelle gamme plus large d’index aléatoires.

 // 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; } 

Autre chose: vous pouvez utiliser la reflection. Si vous mettez cela en cache correctement, cela clone 1 000 000 d’objects en 5,6 secondes (malheureusement, 16,4 secondes avec des objects internes).

 [ProtoContract(ImplicitFields = ImplicitFields.AllPublic)] public class Person { ... Job JobDescription ... } [ProtoContract(ImplicitFields = ImplicitFields.AllPublic)] public class Job {... } private static readonly Type ssortingngType = typeof (ssortingng); public static class CopyFactory { static readonly Dictionary ProperyList = new Dictionary(); private static readonly MethodInfo CreateCopyReflectionMethod; static CopyFactory() { CreateCopyReflectionMethod = typeof(CopyFactory).GetMethod("CreateCopyReflection", BindingFlags.Static | BindingFlags.Public); } public static T CreateCopyReflection(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 != ssortingngType ? CreateCopyReflectionMethod.MakeGenericMethod(prop.PropertyType).Invoke(null, new object[] { value }) : value, null); } return copyInstance; } 

Je l’ai mesuré de manière simple, en utilisant la classe 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; 

RÉSULTAT: Avec l'object interne PersonInstance - 16.4, PersonInstance = null - 5.6

CopyFactory est juste ma classe de test où j'ai une douzaine de tests, y compris l'utilisation de l'expression. Vous pouvez l'implémenter sous une autre forme dans une extension ou autre. N'oubliez pas la mise en cache.

Je n'ai pas encore testé de sérialisation, mais je doute d'une amélioration avec un million de classes. Je vais essayer quelque chose de rapide protobuf / newton.

PS: pour simplifier la lecture, je n'ai utilisé que la propriété auto ici. Je pourrais mettre à jour avec FieldInfo, ou vous devriez facilement l'implémenter par vos propres moyens.

J'ai récemment testé le sérialiseur Protocol Buffers avec la fonction DeepClone prête à l'emploi. Il gagne avec 4,2 secondes sur un million d'objects simples, mais lorsqu'il s'agit d'objects intérieurs, il gagne avec le résultat 7,4 secondes.

 Serializer.DeepClone(personList); 

RÉSUMÉ: Si vous n'avez pas access aux classes, cela vous aidera. Sinon, cela dépend du nombre d'objects. Je pense que vous pourriez utiliser la reflection jusqu'à 10 000 objects (peut-être un peu moins), mais pour plus que cela, le sérialiseur Protocol Buffers fonctionnera mieux.

Il existe un moyen simple de cloner des objects en C # en utilisant un sérialiseur et un désérialiseur JSON.

Vous pouvez créer une classe d’extension:

 using Newtonsoft.Json; static class typeExtensions { [Extension()] public static T jsonCloneObject(T source) { ssortingng json = JsonConvert.SerializeObject(source); return JsonConvert.DeserializeObject(json); } } 

Pour cloner et object:

 obj clonedObj = originalObj.jsonCloneObject; 
  //try this List ListCopy= new List(OldList); //or try List ListCopy=OldList.ToList();