wake-up-neo.net

Was ist der beste Weg, ein generisches .NET-Wörterbuch <string, T> zu klonen/tief zu kopieren?

Ich habe ein generisches Wörterbuchwörterbuch, das ich im Wesentlichen machen möchte. 

169
mikeymo

Okay, das .NET 2.0 antwortet:

Wenn Sie die Werte nicht klonen müssen, können Sie die Konstruktorüberladung für Dictionary verwenden, wodurch ein vorhandenes IDictionary verwendet wird. (Sie können den Vergleicher auch als Vergleicher des vorhandenen Wörterbuchs angeben.)

Wenn Sie do die Werte klonen müssen, können Sie Folgendes verwenden:

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

Dies setzt natürlich voraus, dass TValue.Clone() ein entsprechend tiefer Klon ist.

162
Jon Skeet

(Anmerkung: Obwohl die Klonversion möglicherweise nützlich ist, ist der Konstruktor, den ich in dem anderen Beitrag erwähnte, für eine einfache flache Kopie eine bessere Option.)

Wie tief soll die Kopie sein und welche Version von .NET verwenden Sie? Ich vermute, dass ein LINQ-Aufruf von ToDictionary, der sowohl die Schlüssel- als auch die Elementauswahl angibt, der einfachste Weg ist, wenn Sie .NET 3.5 verwenden.

Wenn Sie beispielsweise nichts dagegen haben, dass der Wert ein flacher Klon ist:

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

Wenn Sie bereits T zur Implementierung von ICloneable eingeschränkt haben:

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

(Die sind nicht getestet, sollten aber funktionieren.)

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

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

Für .NET 2.0 können Sie eine Klasse implementieren, die von Dictionary erbt und ICloneable implementiert.

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

Sie können das Wörterbuch dann einfach klonen, indem Sie die Clone-Methode aufrufen. Natürlich erfordert diese Implementierung, dass der Werttyp des Wörterbuchs ICloneable implementiert, ansonsten ist eine generische Implementierung jedoch nicht praktisch.

10
Compile This

Sie können immer die Serialisierung verwenden. Sie könnten das Objekt serialisieren und dann deserialisieren. Dadurch erhalten Sie eine tiefe Kopie des Wörterbuchs und aller darin enthaltenen Elemente. Jetzt können Sie eine tiefe Kopie eines Objekts erstellen, das als [Serializable] markiert ist, ohne einen speziellen Code zu schreiben.

Hier sind zwei Methoden, die die binäre Serialisierung verwenden. Wenn Sie diese Methoden verwenden, rufen Sie einfach auf 

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

Der beste Weg für mich ist dieser:

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

Die binäre Serialisierungsmethode funktioniert gut, aber in meinen Tests zeigte sich, dass sie 10x langsamer ist als eine nicht-Serialisierungsimplementierung des Klons. Getestet auf Dictionary<string , List<double>>

3
loty

Das funktioniert gut für mich

 // 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

Ich habe auf alte Post geantwortet, aber ich fand es nützlich, es wie folgt zu verpacken:

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

Versuchen Sie dies, wenn Schlüssel/Werte ICloneable sind:

    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