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

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

Ответы на вопрос(10)

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

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

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

 Stephan Ryer16 янв. 2019 г., 11:36
Это серьезно нуждается в голосовании! Однако если исходный словарь доступен только для чтения, он все равно будет работать: var newDict = readonlyDict.ToDictionary (kvp => kvp.Key, kvp => kvp.Value)
 Tomer Wolberg02 мар. 2019 г., 21:23
Это неработает, если тип значения является изменяемым классом

ъект, а затем десериализовать его. Это даст вам глубокую копию словаря и всех предметов внутри него. Теперь вы можете создать полную копию любого объекта, помеченного как [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;
}

    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>

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

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

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

 user42066726 авг. 2016 г., 01:06
По умолчанию LINQ 's ToDictionary не делаетСкопируйте компаратор. Вы упомянули копирование компаратора в другом ответе, но яЯ думаю, что эта версия клонирования также должна пройти сравнение.
 mikeymo26 сент. 2008 г., 16:09
Спасибо за ответ, Джон. Я'м фактически использует v2.0 фреймворка.
 Pratik30 июл. 2013 г., 06:58
Что такое "запись => entry.Key, entry => entry.Value» в данном контексте. Как я добавлю ключ и значение. Это показывает ошибку в моем конце
 Jon Skeet30 июл. 2013 г., 07:51
@Pratik: Они 'ре лямбда-выражения - часть C # 3.

который наследуется от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, но в противном случае универсальная реализация неэто вообще практично.

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

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

Dictionary copy = new Dictionary(dictionary);
 Tomas Kubes20 мар. 2019 г., 09:05
Это не работает в .NET Standard 2.0
 Omid-RH15 дек. 2018 г., 09:17
это работает, и это хорошо, что не клонирует ссылочные объекты! Мы хотим иметь клон словаря, а не ссылочные значения.
 user162587102 янв. 2014 г., 12:47
@FokkoDriesprong нет, он просто копирует keyValuePairs в новый объект
 Jens09 мая 2016 г., 14:06
@ UGКогда вы забыли протестировать фактическое изменение ссылочного объекта, вы проверяете замену указателей значений в клонированных словарях, что, очевидно, работает, но ваши тесты не пройдут, если вы просто измените свойства своих тестовых объектов, например:dotnetfiddle.net/xmPPKr
 MonsterMMORPG03 сент. 2015 г., 22:41
@Contango, так что в этом случае, поскольку string и int НЕ являются ссылочными типами, это будет работать правильно?
 Uğur Aldanmaz20 дек. 2015 г., 14:07
Нет, это работает и ссылочные типы. Мой тест здесь:dotnetfiddle.net/ZNVqfL
 Contango12 мая 2014 г., 15:57
Это определенно хорошо работает - создает клон как ключа, так и значения. Конечно, это работает только в том случае, если значение НЕ является ссылочным типом, если значение является ссылочным типом, тогда оно фактически принимает только копию ключей в качестве мелкой копии.
 Aaron Franke14 авг. 2018 г., 19:39
Это хорошо работает, если вы используете примитивы или структуры.
 Fokko Driesprong25 апр. 2012 г., 11:21
Указатели значений остаются прежними, если вы примените изменения к значениям в копии, изменения также будут отражены в объекте словаря.

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

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

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

 Saeed Ganji16 апр. 2019 г., 09:20
 "использовать перегрузку конструктора для словаря, который принимает существующий IDictionary " Это хорошо" <    Это также потокобезопасно, верно? Мне не требуется добавлять объект блокировки при клонировании?
 Saeed Ganji15 апр. 2019 г., 15:53
Уважаемый @JonSkeet, как насчет следующего подхода?stackoverflow.com/questions/5963115/...  Какой из них вы предлагаете?
 ChrisW04 янв. 2017 г., 16:27
Я думаю что'однако, мы делаем только поверхностную копию значений словаря.entry.Value значение может быть еще одной [под] коллекцией.
 Jon Skeet15 апр. 2019 г., 16:10
@SaeedGanji: Это должно быть хорошо, да. (Конечно, если структуры содержат ссылки на изменяемые ссылочные типы, это все еще может быть проблемой ... но, надеюсь, это 'не тот случай.)>
 Saeed Ganji15 апр. 2019 г., 16:08
@JonSkeet Итак, если я оставлю в словаре объекты, которые являются структурами, а не классами, подход конструктора удовлетворителен, верно?
 Jon Skeet16 апр. 2019 г., 09:34
@SaeedGanji: если тыповторное клонированиеот ConcurrentDictionary, это должно быть хорошо. Но на данный момент я думаю, что если у вас есть какие-либо дополнительные вопросы, вы должны задать в новом сообщении, а не добавлять комментарии здесь.
 Saeed Ganji16 апр. 2019 г., 09:43
 Saeed Ganji16 апр. 2019 г., 09:29
Уважаемый @JonSkeet, спасибо за ваши быстрые ответы, я использую ConcurrentDictionary, но в какой-то момент мне нужно клонировать его значения и очистить его, после операции клонирования мне нужно продолжить клонированный, в то время как другие потоки пишут с высокой скорость на основном CD. Я не хочу пропустить какие-либо ценности
 Jon Skeet16 апр. 2019 г., 09:25
@SaeedGanji: Это зависит от того, что еще происходит. Если другие темы толькочтение из оригинального словаря, то я считаю, что это должно быть хорошо. Если что-то меняет это, вы 'Вам нужно будет заблокировать как этот поток, так и поток клонирования, чтобы избежать их одновременного выполнения. Если вы хотите безопасность потоков при использовании словарей, используйте.ConcurrentDictionary
 Jon Skeet15 апр. 2019 г., 15:55
@SaeedGanji: Хорошо, если значения нене нужно клонировать, "использовать перегрузку конструктора для словаря, который принимает существующий IDictionary " хорошо, и уже в моем ответе. Если значенияделать нужно клонировать, тогда ответь тебесвязан с нене поможет вообще.
 Jon Skeet04 янв. 2017 г., 16:39
@ChrisW: Хорошо, что 'требует, чтобы каждое значение было клонировано - этодоClone() метод ли этоглубоко или неглубоко. Я'Мы добавили примечание на этот счет.

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

Dictionary<int, int=""> copy= new Dictionary<int, int="">(yourListOrDictionary);
</int,></int,>
 Goku24 нояб. 2018 г., 02:03
разве это не просто копирование ссылки, а не значений, поскольку словарь является ссылочным типом? что означает, что если вы измените значения в одном, это изменит значение в другом?
 RredCat23 сент. 2015 г., 14:30
Вестник Смит уже писал об этом.

Ваш ответ на вопрос