721 votes

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

J'ai une liste générique d'objets en C#, et je souhaite cloner cette liste. Les éléments de la liste peuvent être clonés, mais il ne semble pas y avoir d'option pour le faire. list.Clone() .

Existe-t-il un moyen simple de contourner ce problème ?

54 votes

Vous devez dire si vous cherchez une copie profonde ou une copie superficielle.

13 votes

Que sont les copies profondes et superficielles ?

6 votes

655voto

Jeff Yates Points 36725

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

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

Cependant, s'il s'agit de types de référence et que vous voulez une copie profonde (en supposant que vos éléments implémentent correctement l'option ICloneable ), vous pourriez faire quelque chose comme ceci :

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

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

De toute évidence, il faut remplacer ICloneable dans les génériques ci-dessus et l'utiliser avec n'importe quel type d'élément qui implémente ICloneable .

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

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

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

Personnellement, j'éviterais ICloneable en raison de la nécessité de garantir une copie profonde de tous les membres. A la place, je suggérerais le constructeur de copie ou une méthode de fabrique comme YourType.CopyFrom(YourType itemToCopy) qui renvoie une nouvelle instance de YourType .

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

1 votes

Je pense que List<T>.ConvertAll pourrait être plus agréable que de créer une nouvelle liste et de faire un foreach+add.

0 votes

Bon point. Je dois admettre que je suis encore en train de me familiariser avec tous les appels LINQ.

0 votes

+1 Donc, en résumé, il est impossible de fournir une fonction de clonage profond pour une Generic.List. Est-ce bien cela ?

448voto

ajm Points 1459

Vous pouvez utiliser une méthode d'extension.

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

76 votes

Je pense que List.ConvertAll pourrait le faire plus rapidement, puisqu'il peut pré-allouer le tableau entier pour la liste, au lieu de devoir le redimensionner tout le temps.

2 votes

@MichaelGG, que se passe-t-il si vous ne voulez pas convertir mais juste cloner/dupliquer les éléments de la liste ? Cela fonctionnerait-il ? || var clonedList = ListOfStrings.ConvertAll(p => p) ;

31 votes

@IbrarMumtaz : C'est la même chose que var clonedList = new List<string>(ListOfStrings) ;

106voto

Anthony Potts Points 2758

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

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

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

Cité par : Recettes de génériques

61 votes

Vous pouvez également réaliser ceci en utilisant le constructeur de la List<T> pour spécifier une List<T> à partir de laquelle copier. eg var shallowClonedList = new List<MyObject>(originalList) ;

13 votes

J'utilise souvent List<int> newList = oldList.ToList() . Même effet. Cependant, la solution d'Arkiliknam est la meilleure pour la lisibilité à mon avis.

2 votes

@DanBechard Des années plus tard, mais je préfère ToList car il évite toutes les redondances - je me demande lequel est le plus performant... j'ai regardé. Regarde la liste ToList appelle new List<T> qui utilisera finalement Array.CopyTo donc à peu près la même chose.

94voto

Patrick Desjardins Points 51478
public static object DeepClone(object obj) 
{
    object objResult = null;

    using (var ms = new MemoryStream())
    {
        var 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 objet doit être [Serializable()] . L'objectif est de perdre toutes les références et d'en construire de nouvelles.

14 votes

+1 - j'aime cette réponse - elle est rapide, sale, méchante et très efficace. J'ai utilisé en silverlight, et j'ai utilisé le DataContractSerializer car le BinarySerializer n'était pas disponible. Qui a besoin d'écrire des pages de code de clonage d'objet quand on peut simplement faire ça ? :)

3 votes

J'aime ça. Même si c'est bien de faire les choses "bien", le "vite fait bien fait" est souvent utile.

3 votes

Vite ! Mais : Pourquoi sale ?

45voto

Nnabike Okaro Points 311

Ou vous pouvez simplement utiliser ToList() sur la liste, qui fait une copie de la liste et la transmet à la nouvelle liste.

var newList = oldList.ToList();

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X