Каков наилучший способ клонирования/глубокой копии .NET generic Dictionary <string, T>?

У меня есть универсальный словарь Dictionary<string, T> который я хотел бы по сути сделать Clone() из... любых предложений.

Ответ 1

Хорошо, ответы .NET 2.0:

Если вам не нужно клонировать значения, вы можете использовать перегрузку конструктора для словаря, который принимает существующий IDictionary. (Вы также можете указать сравнительный словарь как существующий словарь.)

Если вам нужно клонировать значения, вы можете использовать что-то вроде этого:

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

Это зависит от того, что TValue.Clone() также является подходящим глубоким клоном, конечно.

Ответ 2

(Примечание: хотя версия для клонирования потенциально полезна, для простой мелкой копии конструктор, который я упоминаю в другом сообщении, является лучшим вариантом.)

Насколько глубока ваша копия, и какую версию .NET вы используете? Я подозреваю, что вызов LINQ ToDictionary, определяющий как селектор ключей, так и элемент, будет самым простым способом, если вы используете .NET 3.5.

Например, если вы не против того, чтобы значение было мелким клоном:

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

Если вы уже ограничили T для реализации ICloneable:

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

(Те непроверены, но должны работать.)

Ответ 3

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

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

Ответ 4

Для .NET 2.0 вы можете реализовать класс, который наследует от Dictionary и реализует 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;
    }
}

Затем вы можете клонировать словарь просто путем вызова метода Clone. Конечно, для этой реализации требуется, чтобы тип значения словаря реализовал ICloneable, но в противном случае общая реализация практически не практична.

Ответ 5

Вы всегда можете использовать сериализацию. Вы можете сериализовать объект, а затем десериализовать его. Это даст вам глубокую копию Словаря и всех элементов внутри него. Теперь вы можете создать глубокую копию любого объекта, помеченного как [Serializable], без написания специального кода.

Вот два метода, которые будут использовать двоичную сериализацию. Если вы используете эти методы, вы просто вызываете

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

Ответ 6

Лучший способ для меня - это:

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

Ответ 7

Метод двоичной сериализации работает отлично, но в моих тестах он оказался на 10 раз медленнее, чем реализация несериализации клонирования. Протестировано на Dictionary<string , List<double>>

Ответ 8

Это прекрасно работает для меня

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

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

Как описывает Томер Вольберг в комментариях, это не работает, если тип значения является изменяемым классом.

Ответ 9

Попробуйте, если ключ/значения 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;
    }

Ответ 10

Отвечая на старый пост, я счел полезным обернуть его следующим образом:

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