2008-09-26 9 views

risposta

147

Va bene, le risposte di .NET 2.0:.

Se non è necessario cl uno dei valori, è possibile utilizzare il sovraccarico del costruttore nel Dizionario che accetta un IDictionary esistente. (È possibile specificare l'operatore di confronto come di confronto del dizionario esistente, anche.)

Se fai necessità di clonare i valori, è possibile utilizzare qualcosa di simile:

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

che si basa su TValue.Clone() essere un clone opportunamente profondo, naturalmente.

+0

Penso che stia facendo solo una copia superficiale dei valori del dizionario, comunque. Il valore 'entry.Value' potrebbe essere ancora un altro [sotto] insieme. – ChrisW

+3

@ChrisW: Beh, questo è chiedere a ciascun valore di essere clonato: dipende dal metodo 'Clone()' che sia profondo o poco profondo. Ho aggiunto una nota in tal senso. –

136

(Nota: anche se la versione clonazione è potenzialmente utile, per una semplice copia superficiale costruttore ho citato in un altro post è una scelta migliore.)

Come profondo cosa vuoi la copia di essere, e quali versione di .NET stai usando? Sospetto che una chiamata LINQ a ToDictionary, che specifica sia la chiave che il selettore di elementi, sarà il modo più semplice per andare se si utilizza .NET 3.5.

Per esempio, se non ti dispiace il valore di essere un clone superficiale:

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

Se hai già costretti T per implementare ICloneable:

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

(Quelle sono testati, ma dovrebbe funzionare)

+0

Grazie per la risposta Jon. In realtà sto usando la v2.0 del framework. – mikeymo

+0

Che cosa è "entry => entry.Key, entry => entry.Value" in questo contesto. Come aggiungerò chiave e valore. Mostra un errore sul mio terminale – Pratik

+2

@Pratik: sono espressioni lambda - parte di C# 3. –

10

Per .NET 2.0 è possibile implementare una classe che eredita da Dictionary 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; 
    } 
} 

È quindi possibile clonare il dizionario semplicemente chiamando il metodo Clone. Ovviamente questa implementazione richiede che il tipo di valore del dizionario implementi ICloneable, ma in caso contrario un'implementazione generica non è affatto pratica.

5

È sempre possibile utilizzare la serializzazione. Potresti serializzare l'oggetto e poi deserializzarlo. Ciò ti darà una copia profonda del dizionario e di tutti gli elementi al suo interno. Ora puoi creare una copia profonda di qualsiasi oggetto contrassegnato come [Serializable] senza scrivere alcun codice speciale.

Ecco due metodi che utilizzeranno la serializzazione binaria. Se si utilizzano questi metodi è sufficiente chiamare

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

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

+0

Sei sicuro di aver fatto una copia completa? Sia le stringhe che gli elenchi devono essere copiati in profondità. Ci sono anche alcuni bug nella versione di serializzazione che lo rendono lento: in 'ToBinary()' il metodo 'Serialize()' viene chiamato con 'this' invece di' yourDictionary'. Quindi in 'FromBinary()' il byte [] viene prima copiato manualmente su MemStream ma può essere solo fornito al suo costruttore. – Jupiter

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

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

I puntatori dei valori sono sempre gli stessi, se applicate le modifiche ai valori in copia, le modifiche si rifletteranno anche sull'oggetto dizionario. –

+1

@FokkoDriesprong no, non fa altro, copia semplicemente keyValuePairs nel nuovo oggetto –

+9

Ciò sicuramente funziona bene - crea un clone sia della chiave che del valore. Naturalmente, questo funziona solo se il valore NON è un tipo di riferimento, se il valore è un tipo di riferimento, quindi in realtà utilizza solo una copia dei tasti come copia superficiale. – Contango

1

Il modo migliore per me è questa:

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

Herald Smit ha scritto già su questo modo. – RredCat

0

Prova questa 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; 
    } 
0

Rispondendo sul vecchio post Tuttavia ho trovato che utile per 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; 
    } 
} 
Problemi correlati