Liste de clonage

Je pensais que pour cloner une liste, il suffisait d’appeler:

List cloneList = new List(originalList); 

Mais j’ai essayé cela dans mon code et je semble avoir des effets qui impliquent que ce qui précède est simplement en train de faire:

cloneList = originalList … car les modifications apscopes à cloneList semblent avoir une incidence sur originalList.

Alors, quel est le moyen de cloner une liste?

MODIFIER:

Je pense faire quelque chose comme ça:

 public static List Clone(this List originalList) where T : ICloneable { return originalList.ConvertAll(x => (T) x.Clone()); } 

EDIT2:

J’ai pris le code de copie en profondeur suggéré par Binoj Antony et créé cette méthode d’extension:

 public static T DeepCopy(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:

Supposons maintenant que les éléments de la liste sont des structures. Quel serait alors le résultat si j’appelais?

 List cloneList = new List(originalList); 

Je suis assez sûr que je voudrais obtenir une liste remplie de nouveaux éléments uniques, correct?

Vous pouvez utiliser le code ci-dessous pour créer une copie complète de la liste ou de tout autre object prenant en charge la sérialisation:

Vous pouvez également l’utiliser pour toutes les versions de .NET Framework à partir de la version 2.0 et ultérieure, et une technique similaire peut être appliquée (suppression de l’utilisation de génériques) et également utilisée dans la version 1.1.

 public static class GenericCopier { 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); } } } 

Vous pouvez l’appeler en utilisant

 List deepCopiedList = GenericCopier>.DeepCopy(originalList); 

Code complet pour vérifier si cela fonctionne:

 static void Main(ssortingng[] args) { List originalList = new List(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 deepCopiedList = GenericCopier>.DeepCopy(originalList); for (int i = 0; i < 5; i++) Console.WriteLine("deepCopiedList[{0}] value is {1}", i, deepCopiedList[i]); } 

Cela fonctionnerait …

 List cloneList = new List(originalList); 

Lorsque vous dites “car les modifications apscopes à cloneList semblent affecter l’originalList”. – voulez-vous dire des changements dans la liste , ou des changements dans les éléments

Ajouter / supprimer / échanger des éléments change la liste – alors si nous le faisons:

 cloneList.Add(anotherItem); 

vous devriez trouver que cloneList est plus long que originalList . Cependant, si les contenus sont des types référence (classes), les deux listes pointent toujours sur les mêmes objects sous-jacents – donc si nous faisons:

 cloneList[0].SomeObjectProperty = 12345; 

alors cela sera également originalList[0].SomeObjectProperty contre originalList[0].SomeObjectProperty – il n’existe qu’un seul object (partagé entre les deux listes).

Si tel est le problème, vous devrez cloner les objects de la liste – et vous entrerez ensuite dans le problème des problèmes profonds et superficiels … Est-ce le problème?

Pour une copie superficielle, vous pourrez peut-être utiliser quelque chose qui ressemblera beaucoup à la réponse – simplement avec TTo = TFrom (peut-être simplifier à un seul T ).

Je doute que votre exemple actuel pose des problèmes, car int est un type de valeur. Par exemple:

 using System; using System.Collections.Generic; class Test { static void Main() { List originalList = new List { 5, 6, 7 }; List cloneList = new List(originalList); cloneList.Add(8); cloneList[0] = 2; Console.WriteLine(originalList.Count); // Still 3 Console.WriteLine(originalList[0]); // Still 5 } } 

Toutefois, comme le dit Marc, si votre liste contient des types de référence modifiables, le clonage de la liste n’en fera qu’une copie superficielle . Si vous modifiez les objects auxquels les listes se réfèrent, ces modifications seront visibles via les deux listes. Le remplacement d’éléments dans une liste ne changera pas l’élément équivalent dans l’autre liste, toutefois:

 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 originalList = new List { new Dummy(5), new Dummy(6), new Dummy(7) }; List cloneList = new List(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 } } 

Pour prendre un “clone profond” d’une liste où le type d’élément implémente ICloneable , utilisez:

 List cloneList = originalList.ConvertAll(x => (Foo) x.Clone()); 

Cependant, la profondeur réelle de ce clone dépendra de la mise en œuvre de ICloneable dans le type d’élément – ICloneable est généralement considéré comme une mauvaise chose car son contrat est si vague.

L’utilisation du constructeur List avec la liste d’origine en tant que paramètre fonctionnera si le type sous-jacent de la liste est un type valeur . Pour les éléments List de type référence, je pense que vous souhaitez les copier en profondeur .

Vous pouvez faire quelque chose comme ça:

(En supposant que le type sous-jacent implémente ICloneable)

 originalList.ForEach((item) => { cloneList.Add((ICloneable)item.Clone()); } ); 

Ou en utilisant un LINQ :

 var cloneList = originalList.Select(item => (ICloneable)item.Clone()).ToList(); 

Je vote pour ne pas compter sur la sérialisation d’object. C’est une pratique coûteuse et mauvaise.

 public static TObj CloneObject(this TObj obj) where TObj : ICloneable { return (TObj)obj.Clone(); } 

La méthode ci-dessus est beaucoup plus élégante, et vous devriez vraiment mettre en place une interface clonable si vous en avez besoin. Vous pouvez également le rendre générique.

 public interface ICloneable : IClonable { T CloneObject(); } 

En option, vous pouvez vous abstenir d’utiliser l’interface IClonable comme type de base car elle est mal entretenue. Le nom de la méthode doit être remplacé par, car vous ne pouvez pas surcharger les types de retour.

 public static List CloneList(this List source) where TObj : ICloneable { return source.Select(x=>x.CloneObject()).ToList(); } 

C’est aussi simple que ça.

Peut-être que votre problème peut être résolu en utilisant plutôt des types de valeur. Ils sont toujours passés par copie. Donc, vous n’avez jamais à cloner quoi que ce soit tant que votre structure de données est en valeur.

Il est spécifiquement indiqué ici que les éléments sont copiés dans votre nouvelle liste. Alors oui, ça devrait marcher. Avec les types de valeur, vous obtiendrez une indépendance complète. Mais rappelez-vous, avec les types de référence, les listes seront indépendantes mais elles pointeront vers les mêmes objects. Vous devrez copier en profondeur la liste.

 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); 

Ce code fonctionne parfaitement comme prévu pour moi. Etes-vous peut-être en train de changer les objects dans la liste? Les éléments de la liste ne seront pas clonés par la new List(oldList) .

Je dois append: si vous souhaitez utiliser la sérialisation pour faciliter la copie en profondeur, pourquoi cloner chaque élément individuellement? Il suffit de cloner la liste originale complète pour commencer.

Sauf si la logique est en place pour cloner uniquement les nœuds qui répondent à certains critères, faites-le nœud par nœud.