web-dev-qa-db-fra.com

Quel est le meilleur moyen de cloner/copier en profondeur un dictionnaire générique .NET <string, T>?

J'ai un dictionnaire générique Dictionnaire que je voudrais essentiellement faire un Clone () de ..toutes suggestions. 

169
mikeymo

OK, le .NET 2.0 répond:

Si vous n'avez pas besoin de cloner les valeurs, vous pouvez utiliser la surcharge de constructeur dans Dictionary, qui utilise un IDictionary existant. (Vous pouvez également spécifier le comparateur en tant que comparateur du dictionnaire existant.)

Si vous do devez cloner les valeurs, vous pouvez utiliser quelque chose comme ceci:

public static Dictionary<TKey, TValue> CloneDictionaryCloningValues<TKey, TValue>
   (Dictionary<TKey, TValue> original) where TValue : ICloneable
{
    Dictionary<TKey, TValue> ret = new Dictionary<TKey, TValue>(original.Count,
                                                            original.Comparer);
    foreach (KeyValuePair<TKey, TValue> entry in original)
    {
        ret.Add(entry.Key, (TValue) entry.Value.Clone());
    }
    return ret;
}

Cela dépend bien sûr de TValue.Clone() étant un clone convenablement profond.

162
Jon Skeet

(Remarque: bien que la version de clonage soit potentiellement utile, pour une copie simple superficielle, le constructeur que je mentionne dans l'autre message est une meilleure option.)

À quelle profondeur voulez-vous que la copie soit, et quelle version de .NET utilisez-vous? Je soupçonne qu'un appel LINQ à ToDictionary, spécifiant à la fois la clé et le sélecteur d'élément, sera le moyen le plus simple à utiliser si vous utilisez .NET 3.5.

Par exemple, si cela ne vous dérange pas que la valeur soit un clone superficiel:

var newDictionary = oldDictionary.ToDictionary(entry => entry.Key,
                                               entry => entry.Value);

Si vous avez déjà contraint T à mettre en œuvre ICloneable:

var newDictionary = oldDictionary.ToDictionary(entry => entry.Key, 
                                               entry => (T) entry.Value.Clone());

(Ceux-ci ne sont pas testés, mais devraient fonctionner.)

168
Jon Skeet
Dictionary<string, int> dictionary = new Dictionary<string, int>();

Dictionary<string, int> copy = new Dictionary<string, int>(dictionary);
68
Herald Smit

Pour .NET 2.0, vous pouvez implémenter une classe qui hérite de Dictionary et implémente ICloneable.

public class CloneableDictionary<TKey, TValue> : Dictionary<TKey, TValue> where TValue : ICloneable
{
    public IDictionary<TKey, TValue> Clone()
    {
        CloneableDictionary<TKey, TValue> clone = new CloneableDictionary<TKey, TValue>();

        foreach (KeyValuePair<TKey, TValue> pair in this)
        {
            clone.Add(pair.Key, (TValue)pair.Value.Clone());
        }

        return clone;
    }
}

Vous pouvez ensuite cloner le dictionnaire en appelant simplement la méthode Clone. Bien sûr, cette implémentation nécessite que le type de valeur du dictionnaire implémente ICloneable, mais sinon, une implémentation générique n’est pas du tout pratique.

10
Compile This

Vous pouvez toujours utiliser la sérialisation. Vous pouvez sérialiser l'objet puis le désérialiser. Cela vous donnera une copie complète du dictionnaire et de tous les éléments qu'il contient. Vous pouvez maintenant créer une copie complète de tout objet portant la mention [Serializable] sans écrire de code spécial.

Voici deux méthodes qui utiliseront la sérialisation binaire. Si vous utilisez ces méthodes, vous appelez simplement 

object deepcopy = FromBinary(ToBinary(yourDictionary));

public Byte[] ToBinary()
{
  MemoryStream ms = null;
  Byte[] byteArray = null;
  try
  {
    BinaryFormatter serializer = new BinaryFormatter();
    ms = new MemoryStream();
    serializer.Serialize(ms, this);
    byteArray = ms.ToArray();
  }
  catch (Exception unexpected)
  {
    Trace.Fail(unexpected.Message);
    throw;
  }
  finally
  {
    if (ms != null)
      ms.Close();
  }
  return byteArray;
}

public object FromBinary(Byte[] buffer)
{
  MemoryStream ms = null;
  object deserializedObject = null;

  try
  {
    BinaryFormatter serializer = new BinaryFormatter();
    ms = new MemoryStream();
    ms.Write(buffer, 0, buffer.Length);
    ms.Position = 0;
    deserializedObject = serializer.Deserialize(ms);
  }
  finally
  {
    if (ms != null)
      ms.Close();
  }
  return deserializedObject;
}
5
Shaun Bowe

Le meilleur moyen pour moi est la suivante:

Dictionary<int, int> copy= new Dictionary<int, int>(yourListOrDictionary);
4
nikssa23

La méthode de sérialisation binaire fonctionne bien, mais lors de mes tests, elle a été 10 fois plus lente qu'une implémentation de clone sans sérialisation Testé le Dictionary<string , List<double>>

3
loty

Cela fonctionne bien pour moi

 // assuming this fills the List
 List<Dictionary<string, string>> obj = this.getData(); 

 List<Dictionary<string, string>> objCopy = new List<Dictionary<string, string>>(obj);
2
BonifatiusK

En répondant sur le vieux post, je trouvais toutefois utile de l’envelopper comme suit:

using System;
using System.Collections.Generic;

public class DeepCopy
{
  public static Dictionary<T1, T2> CloneKeys<T1, T2>(Dictionary<T1, T2> dict)
    where T1 : ICloneable
  {
    if (dict == null)
      return null;
    Dictionary<T1, T2> ret = new Dictionary<T1, T2>();
    foreach (var e in dict)
      ret[(T1)e.Key.Clone()] = e.Value;
    return ret;
  }

  public static Dictionary<T1, T2> CloneValues<T1, T2>(Dictionary<T1, T2> dict)
    where T2 : ICloneable
  {
    if (dict == null)
      return null;
    Dictionary<T1, T2> ret = new Dictionary<T1, T2>();
    foreach (var e in dict)
      ret[e.Key] = (T2)(e.Value.Clone());
    return ret;
  }

  public static Dictionary<T1, T2> Clone<T1, T2>(Dictionary<T1, T2> dict)
    where T1 : ICloneable
    where T2 : ICloneable
  {
    if (dict == null)
      return null;
    Dictionary<T1, T2> ret = new Dictionary<T1, T2>();
    foreach (var e in dict)
      ret[(T1)e.Key.Clone()] = (T2)(e.Value.Clone());
    return ret;
  }
}
0
Decaf Sux

Essayez ceci si la clé/les valeurs sont identifiables:

    public static Dictionary<K,V> CloneDictionary<K,V>(Dictionary<K,V> dict) where K : ICloneable where V : ICloneable
    {
        Dictionary<K, V> newDict = null;

        if (dict != null)
        {
            // If the key and value are value types, just use copy constructor.
            if (((typeof(K).IsValueType || typeof(K) == typeof(string)) &&
                 (typeof(V).IsValueType) || typeof(V) == typeof(string)))
            {
                newDict = new Dictionary<K, V>(dict);
            }
            else // prepare to clone key or value or both
            {
                newDict = new Dictionary<K, V>();

                foreach (KeyValuePair<K, V> kvp in dict)
                {
                    K key;
                    if (typeof(K).IsValueType || typeof(K) == typeof(string))
                    {
                        key = kvp.Key;
                    }
                    else
                    {
                        key = (K)kvp.Key.Clone();
                    }
                    V value;
                    if (typeof(V).IsValueType || typeof(V) == typeof(string))
                    {
                        value = kvp.Value;
                    }
                    else
                    {
                        value = (V)kvp.Value.Clone();
                    }

                    newDict[key] = value;
                }
            }
        }

        return newDict;
    }
0
Arvind