在.NET中如何克隆字典?

41

我知道我们应该使用字典而不是哈希表。但我找不到一种复制字典的方法。即使将其强制转换为ICollection以获取SyncRoot,也不行,而这也被认为是不好的。

我现在正在进行更改。我的正确假设是,在泛型中无法实现任何类型的克隆,这就是为什么不支持字典克隆的原因吗?


3
请参考这个链接:https://dev59.com/b3VC5IYBdhLWcg3w9F89。 - sashaeve
我看到了,但我不关心深拷贝。克隆浅拷贝就可以了。另外,我的问题还有第二部分:没有克隆的原因是泛型引入的困难吗? - uriDium
7个回答

59

使用接受字典参数的构造函数。请参考此例:

var dict = new Dictionary<string, string>();

dict.Add("SO", "StackOverflow");

var secondDict = new Dictionary<string, string>(dict);

dict = null;

Console.WriteLine(secondDict["SO"]);

只是为了好玩...你可以使用LINQ!这是一种更通用的方法。

var secondDict = (from x in dict
                  select x).ToDictionary(x => x.Key, x => x.Value);

编辑

这应该适用于引用类型,我尝试了以下代码:

internal class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public User Parent { get; set; }
}

上述代码的修改版如下所示

var dict = new Dictionary<string, User>();

dict.Add("First", new User 
    { Id = 1, Name = "Filip Ekberg", Parent = null });

dict.Add("Second", new User 
    { Id = 2, Name = "Test test", Parent = dict["First"] });

var secondDict = (from x in dict
                  select x).ToDictionary(x => x.Key, x => x.Value);

dict.Clear();

dict = null;

Console.WriteLine(secondDict["First"].Name);

这将输出 "Filip Ekberg"。


9
请记住,第一种方法会创建一个浅拷贝,也就是说对象并没有被复制。对于字符串来说这不是问题,但对于其他引用类型可能会有问题。 - Brian Rasmussen
使用LINQ表达式应该至少复制引用。当GC找到dict并且它为null,但是引用不为null时,它们不应该被删除,我是对的吗?因此,它也应该适用于引用类型。 - Filip Ekberg
3
需要注意的是:这种方法不会克隆源IDictionaryIEqualityComparer,即如果您有一个具有StringComparer.OrdinalIgnoreCaseIDictionary - Dennis
第一个例子也不应该写成"dict = null;",而应该写成"dict.clear();"以真正证明新的变量不仅仅是引用。 - Andreas Reiff
4
参考示例错误/误导性 - 引用类型在两个集合中仍然是相同的实例: https://dotnetfiddle.net/AMRtta - 改变 dict["First"].Name,在 secondDict["First"].Name 中也会被改变。从一个集合中删除它并不等同于修改它。另外,如果你要使用 Linq:只需使用 dict.ToDictionary 而不是 (from x in dict select x).ToDictionary - drzaus
这是不正确的。这仍然是一个浅拷贝。请参考这个例子:https://dotnetfiddle.net/SoMRJv - undefined

2
这是我曾经编写的一种快速而简单的克隆方法……最初的想法来自于CodeProject,我想。
Imports System.Runtime.Serialization
Imports System.Runtime.Serialization.Formatters.Binary

Public Shared Function Clone(Of T)(ByVal inputObj As T) As T
    'creating a Memorystream which works like a temporary storeage '
    Using memStrm As New MemoryStream()
        'Binary Formatter for serializing the object into memory stream '
        Dim binFormatter As New BinaryFormatter(Nothing, New StreamingContext(StreamingContextStates.Clone))

        'talks for itself '
        binFormatter.Serialize(memStrm, inputObj)

        'setting the memorystream to the start of it '
        memStrm.Seek(0, SeekOrigin.Begin)

        'try to cast the serialized item into our Item '
        Try
            return DirectCast(binFormatter.Deserialize(memStrm), T)
        Catch ex As Exception
            Trace.TraceError(ex.Message)
            return Nothing
        End Try
    End Using
End Function

使用方法:

Dim clonedDict As Dictionary(Of String, String) = Clone(Of Dictionary(Of String, String))(yourOriginalDict)

2

如果有人需要VB.NET版本,可以参考以下内容:

Dim dictionaryCloned As Dictionary(Of String, String)
dictionaryCloned  = (From x In originalDictionary Select x).ToDictionary(Function(p) p.Key, Function(p) p.Value)

1

对于VB.NET,我发现了一个更简单的解决方案:

dim seconddic as Dictionary(of string,string) = new Dictionary(of string,string)(originaldic)

0

最简单的方法:

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

Dictionary<string, int> newDictionary = new Dictionary<string, int>(oldDictionary);

更简洁的代码:Dim updatedRecord As New Dictionary(Of String, String)(Record) - user10191234

0

对于简单的Dictionary<String, Object>

public static Dictionary<string, object> DictionaryClone(Dictionary<string, object> _Datas)
{
    Dictionary<string, object> output = new Dictionary<string, object>();

    if (_Datas != null)
    {
        foreach (var item in _Datas)
            output.Add(item.Key, item.Value);
    }

    return output;
}

0

对于原始类型的字典

Public Sub runIntDictionary()
  Dim myIntegerDict As New Dictionary(Of Integer, Integer) From {{0, 0}, {1, 1}, {2, 2}}
  Dim cloneIntegerDict As New Dictionary(Of Integer, Integer)
  cloneIntegerDict = myIntegerDict.Select(Function(x) x.Key).ToList().ToDictionary(Of Integer, Integer)(Function(x) x, Function(y) myIntegerDict(y))
End Sub

对于实现ICloneable接口的对象字典

Public Sub runObjectDictionary()
  Dim myDict As New Dictionary(Of Integer, number) From {{3, New number(3)}, {4, New number(4)}, {5, New number(5)}}
  Dim cloneDict As New Dictionary(Of Integer, number)
  cloneDict = myDict.Select(Function(x) x.Key).ToList().ToDictionary(Of Integer, number)(Function(x) x, Function(y) myDict(y).Clone)
End Sub

Public Class number
  Implements ICloneable
  Sub New()
  End Sub
  Sub New(ByVal newNumber As Integer)
    nr = newnumber
  End Sub
  Public nr As Integer

  Public Function Clone() As Object Implements ICloneable.Clone
    Return New number With {.nr = nr}
  End Function
  Public Overrides Function ToString() As String
    Return nr.ToString
  End Function
End Class

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接