Qual è il modo migliore per clonare / copiare in profondità un dizionario generico .NET < string, T > ;?

StackOverflow https://stackoverflow.com/questions/139592

Domanda

Ho un dizionario generico Dictionary < string, T > che vorrei essenzialmente fare un Clone () di ..ogni suggerimento.

È stato utile?

Soluzione

Ok, la risposta di .NET 2.0:

Se non è necessario clonare i valori, è possibile utilizzare il sovraccarico del costruttore su Dizionario che accetta un IDictionary esistente. (Puoi anche specificare il comparatore come comparatore del dizionario esistente.)

Se fai devi clonare i valori, puoi usare qualcosa del genere:

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

Ciò si basa sul fatto che TValue.Clone () sia anche un clone adeguatamente profondo, ovviamente.

Altri suggerimenti

(Nota: sebbene la versione della clonazione sia potenzialmente utile, per una semplice copia superficiale il costruttore che cito nell'altro post è un'opzione migliore.)

Quanto vuoi che sia profonda la copia e quale versione di .NET stai usando? Sospetto che una chiamata LINQ a ToDictionary, specificando sia la chiave che il selettore di elementi, sarà il modo più semplice di procedere se si utilizza .NET 3.5.

Ad esempio, se non ti dispiace che il valore sia un clone superficiale:

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

Se hai già vincolato T per implementare ICloneable:

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

(Quelli non sono testati, ma dovrebbero funzionare.)

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

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

Per .NET 2.0 potresti implementare una classe che eredita da Dizionario e implementa 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;
    }
}

Puoi quindi clonare il dizionario semplicemente chiamando il metodo Clone . Naturalmente questa implementazione richiede che il tipo di valore del dizionario implementi ICloneable , ma per il resto un'implementazione generica non è affatto pratica.

Puoi sempre usare la serializzazione. È possibile serializzare l'oggetto quindi deserializzarlo. Questo ti darà una copia approfondita del Dizionario e di tutti gli elementi al suo interno. Ora puoi creare una copia profonda di qualsiasi oggetto contrassegnato come [serializzabile] senza scrivere alcun codice speciale.

Ecco due metodi che useranno la serializzazione binaria. Se usi questi metodi, chiami semplicemente

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

Il metodo di serializzazione binaria funziona bene, ma nei miei test ha dimostrato di essere 10 volte più lento di un'implementazione non seriale di clone. Testato su Dictionary < string, List < double > >

Il modo migliore per me è questo:

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

Questo funziona bene per me

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

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

Come descritto da Tomer Wolberg nei commenti, questo non funziona se il tipo di valore è una classe mutabile.

Prova questo se chiave / valori sono ICloneable:

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

Rispondendo ai vecchi post, ho trovato utile avvolgerlo come segue:

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;
  }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top