克隆/深度复制.NET泛型字典<string,T>的最佳方法是什么?

mik*_*ymo 191 c# generics collections clone

我有一个通用词典词典,我想基本上做一个克隆()的任何建议.

Jon*_*eet 196

(注意:虽然克隆版本可能有用,但对于一个简单的浅拷贝,我在另一篇文章中提到的构造函数是一个更好的选择.)

您希望副本有多深,以及您使用的是什么版本的.NET?我怀疑如果您使用的是.NET 3.5,那么对ToDictionary进行LINQ调用(同时指定键和元素选择器)将是最简单的方法.

例如,如果您不介意该值是浅层克隆:

var newDictionary = oldDictionary.ToDictionary(entry => entry.Key,
                                               entry => entry.Value);
Run Code Online (Sandbox Code Playgroud)

如果您已经限制T实现ICloneable:

var newDictionary = oldDictionary.ToDictionary(entry => entry.Key, 
                                               entry => (T) entry.Value.Clone());
Run Code Online (Sandbox Code Playgroud)

(这些是未经测试的,但应该有效.)

  • @Pratik:他们是lambda表达式 - C#3的一部分. (2认同)
  • 默认情况下,LINQ的ToDictionary不复制比较器。您在其他答案中提到过复制比较器,但是我认为此版本的克隆也应该通过比较器。 (2认同)

Jon*_*eet 175

好的,.NET 2.0回答:

如果您不需要克隆值,则可以使用构造函数重载到Dictionary,它接受现有的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;
}
Run Code Online (Sandbox Code Playgroud)

当然,这也依赖于TValue.Clone()适当的深度克隆.

  • @ChrisW:那就是要求克隆每个值 - 这取决于`Clone()`方法,无论是深度还是浅度.我已经添加了一个注释. (6认同)
  • @SaeedGanji:好吧,如果不需要克隆值,“使用构造函数重载到字典,它采用现有的 IDictionary”很好,并且已经在我的回答中。如果需要克隆值 *do*,那么您链接到的答案根本没有帮助。 (2认同)

Her*_*mit 79

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

Dictionary<string, int> copy = new Dictionary<string, int>(dictionary);
Run Code Online (Sandbox Code Playgroud)

  • 这绝对有效 - 它创建了键和值的克隆.当然,这仅在值不是引用类型时有效,如果值是引用类型,则它实际上仅将键的副本作为浅副本. (15认同)
  • 值的指针仍然相同,如果对复制中的值应用更改,更改也将反映在字典对象中. (5认同)
  • @UğurAldanmaz你忘记测试对引用对象的实际更改,你只测试克隆字典中值指针的替换,这显然有效,但如果你只更改测试对象的属性,你的测试将失败,如下所示:https:/ /dotnetfiddle.net/xmPPKr (3认同)
  • @FokkoDriesprong不,它只是复制新对象中的keyValuePairs (2认同)

小智 11

当我尝试深度复制 Dictionary < string, string > 时,这对我有帮助

Dictionary<string, string> dict2 = new Dictionary<string, string>(dict);
Run Code Online (Sandbox Code Playgroud)

祝你好运

  • 这会生成一个浅拷贝。如果dict2的值是引用类型,那么dict的值也会被dict2更新。(基于.NET5) (16认同)
  • 适用于 .NET 4.6.1。这应该是更新的答案。 (3认同)

Com*_*his 10

对于.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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您只需调用该Clone方法即可克隆字典.当然,这种实现要求字典的值类型实现ICloneable,但是否则通用实现根本不实用.


Bon*_*usK 8

这对我来说很好用

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

 List<Dictionary<string, string>> objCopy = new List<Dictionary<string, string>>(obj);
Run Code Online (Sandbox Code Playgroud)

正如 Tomer Wolberg 在评论中所描述的,如果值类型是可变类,这将不起作用。

  • 如果值类型是可变类,那将不起作用 (4认同)

Sha*_*owe 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;
}
Run Code Online (Sandbox Code Playgroud)


小智 5

对我来说最好的方法是:

Dictionary<int, int> copy= new Dictionary<int, int>(yourListOrDictionary);
Run Code Online (Sandbox Code Playgroud)

  • 这不是复制引用,而不是值吗,因为Dictionary是引用类型?这意味着如果您更改其中一个的值,将会更改另一个的值? (3认同)
  • 这会生成一个浅拷贝。如果字典的值是引用类型,则仅复制地址值。 (2认同)