Вопрос по generics, collections, clone, c# – Каков наилучший способ клонирования / глубокого копирования универсального словаря .NET <string, T>?

173

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

Ваш Ответ

10   ответов
1

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

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

 List<dictionary<string, string="">> objCopy = new List<dictionary<string, string="">>(obj);
</dictionary<string,></dictionary<string,></dictionary<string,>

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

Это серьезно нуждается в голосовании! Однако если исходный словарь доступен только для чтения, он все равно будет работать: var newDict = readonlyDict.ToDictionary (kvp => kvp.Key, kvp => kvp.Value) Stephan Ryer
Это неработает, если тип значения является изменяемым классом Tomer Wolberg
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;
}
0

    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;
    }
</k,></k,></k,></k,></k,v></k,v></k,v>
169

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

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

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

@Pratik: Они 'ре лямбда-выражения - часть C # 3. Jon Skeet
По умолчанию LINQ 's ToDictionary не делаетСкопируйте компаратор. Вы упомянули копирование компаратора в другом ответе, но яЯ думаю, что эта версия клонирования также должна пройти сравнение. user420667
Спасибо за ответ, Джон. Я'м фактически использует v2.0 фреймворка. mikeymo
Что такое "запись => entry.Key, entry => entry.Value» в данном контексте. Как я добавлю ключ и значение. Это показывает ошибку в моем конце Pratik
10

который наследуется от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;
    }
}
</tkey,></tkey,></tkey,></tkey,></tkey,></tkey,>

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

0

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

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;
  }
}
</t1,></t1,></t1,></t1,></t1,></t1,></t1,></t1,></t1,></t1,></t1,></t1,></t1,></t1,></t1,>
70
Dictionary dictionary = new Dictionary();

Dictionary copy = new Dictionary(dictionary);
Это не работает в .NET Standard 2.0 Tomas Kubes
это работает, и это хорошо, что не клонирует ссылочные объекты! Мы хотим иметь клон словаря, а не ссылочные значения. Omid-RH
@FokkoDriesprong нет, он просто копирует keyValuePairs в новый объект user1625871
@ UGКогда вы забыли протестировать фактическое изменение ссылочного объекта, вы проверяете замену указателей значений в клонированных словарях, что, очевидно, работает, но ваши тесты не пройдут, если вы просто измените свойства своих тестовых объектов, например:dotnetfiddle.net/xmPPKr Jens
3

но в моих тестах он показал, что он в 10 раз медленнее, чем реализация клонов без сериализации. Протестировано наDictionary

Вы уверены, что сделали полную глубокую копию? И строки, и списки должны быть глубоко скопированы. Есть также некоторые ошибки в версии сериализации, из-за которых она работает медленно:ToBinary() Serialize() метод вызывается сthis вместоyourDictionary, Затем вFromBinary() byte [] сначала копируется вручную в MemStream, но его можно просто передать его конструктору. Jupiter
163

Если вы неЧтобы клонировать значения, вы можете использовать перегрузку конструктора для словаря, который принимает существующий 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;
}
</tkey,></tkey,></tkey,></tkey,></tkey,></tkey,>

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

 "использовать перегрузку конструктора для словаря, который принимает существующий IDictionary " Это хорошо" <    Это также потокобезопасно, верно? Мне не требуется добавлять объект блокировки при клонировании? Saeed Ganji
@SaeedGanji: Это зависит от того, что еще происходит. Если другие темы толькочтение из оригинального словаря, то я считаю, что это должно быть хорошо. Если что-то меняет это, вы 'Вам нужно будет заблокировать как этот поток, так и поток клонирования, чтобы избежать их одновременного выполнения. Если вы хотите безопасность потоков при использовании словарей, используйте.ConcurrentDictionary Jon Skeet
@SaeedGanji: Хорошо, если значения нене нужно клонировать, "использовать перегрузку конструктора для словаря, который принимает существующий IDictionary " хорошо, и уже в моем ответе. Если значенияделать нужно клонировать, тогда ответь тебесвязан с нене поможет вообще. Jon Skeet
Уважаемый @JonSkeet, как насчет следующего подхода?stackoverflow.com/questions/5963115/...  Какой из них вы предлагаете? Saeed Ganji
4

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

Dictionary<int, int=""> copy= new Dictionary<int, int="">(yourListOrDictionary);
</int,></int,>
разве это не просто копирование ссылки, а не значений, поскольку словарь является ссылочным типом? что означает, что если вы измените значения в одном, это изменит значение в другом? Goku
Вестник Смит уже писал об этом. RredCat

Похожие вопросы