Каков наилучший способ клонирования / глубокого копирования универсального словаря .NET <string, T>? - PullRequest
180 голосов
/ 26 сентября 2008

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

Ответы [ 10 ]

182 голосов
/ 26 сентября 2008

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

Насколько глубокой должна быть копия, и какую версию .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());

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

170 голосов
/ 26 сентября 2008

Хорошо, ответы .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() также является достаточно глубоким клоном, конечно.

72 голосов
/ 24 ноября 2010
Dictionary<string, int> dictionary = new Dictionary<string, int>();

Dictionary<string, int> copy = new Dictionary<string, int>(dictionary);
10 голосов
/ 26 сентября 2008

Для .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 голосов
/ 26 сентября 2008

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

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

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 голосов
/ 08 сентября 2015

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

Dictionary<int, int> copy= new Dictionary<int, int>(yourListOrDictionary);
3 голосов
/ 30 сентября 2009

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

2 голосов
/ 22 марта 2018

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

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

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

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

0 голосов
/ 14 января 2018

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

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 голосов
/ 23 августа 2016

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