简体   繁体   English

克隆/深度复制 .NET 通用词典的最佳方法是什么<string, t> ?</string,>

[英]What is the best way to clone/deep copy a .NET generic Dictionary<string, T>?

I've got a generic dictionary Dictionary<string, T> that I would like to essentially make a Clone() of..any suggestions.我有一个通用字典Dictionary<string, T>我想从本质上提出一个 Clone() of..any suggestions。

(Note: although the cloning version is potentially useful, for a simple shallow copy the constructor I mention in the other post is a better option.) (注意:虽然克隆版本可能有用,但对于简单的浅拷贝,我在另一篇文章中提到的构造函数是更好的选择。)

How deep do you want the copy to be, and what version of .NET are you using?您希望副本有多深,您使用的是什么版本的 .NET? I suspect that a LINQ call to ToDictionary, specifying both the key and element selector, will be the easiest way to go if you're using .NET 3.5.我怀疑如果您使用 .NET 3.5,对 ToDictionary 的 LINQ 调用(指定键和元素选择器)将是最简单的方法。

For instance, if you don't mind the value being a shallow clone:例如,如果你不介意值是一个浅克隆:

var newDictionary = oldDictionary.ToDictionary(entry => entry.Key,
                                               entry => entry.Value);

If you've already constrained T to implement ICloneable:如果您已经约束 T 来实现 ICloneable:

var newDictionary = oldDictionary.ToDictionary(entry => entry.Key, 
                                               entry => (T) entry.Value.Clone());

(Those are untested, but should work.) (那些未经测试,但应该可以工作。)

Okay, the .NET 2.0 answers:好的,.NET 2.0 回答:

If you don't need to clone the values, you can use the constructor overload to Dictionary which takes an existing IDictionary.如果您不需要克隆值,您可以使用构造函数重载到字典,它采用现有的 IDictionary。 (You can specify the comparer as the existing dictionary's comparer, too.) (您也可以将比较器指定为现有字典的比较器。)

If you do need to clone the values, you can use something like this:如果确实需要克隆值,可以使用以下方法:

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;
}

That relies on TValue.Clone() being a suitably deep clone as well, of course.当然,这也依赖于TValue.Clone()是一个合适的深度克隆。

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

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

That's what helped me, when I was trying to deep copy a Dictionary < string, string >当我尝试深度复制 Dictionary < string, string > 时,这对我有帮助

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

Good luck祝你好运

For .NET 2.0 you could implement a class which inherits from Dictionary and implements ICloneable .对于 .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;
    }
}

You can then clone the dictionary simply by calling the Clone method.然后,您只需调用Clone方法即可克隆字典。 Of course this implementation requires that the value type of the dictionary implements ICloneable , but otherwise a generic implementation isn't practical at all.当然,这个实现需要字典的值类型实现ICloneable ,否则泛型实现根本不实用。

This works fine for me这对我来说很好用

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

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

As Tomer Wolberg describes in the comments, this does not work if the value type is a mutable class.正如 Tomer Wolberg 在评论中所描述的,如果值类型是可变类,这将不起作用。

You could always use serialization.你总是可以使用序列化。 You could serialize the object then deserialize it.您可以序列化对象然后反序列化它。 That will give you a deep copy of the Dictionary and all the items inside of it.这将为您提供字典及其中所有项目的深层副本。 Now you can create a deep copy of any object that is marked as [Serializable] without writing any special code.现在您可以创建任何标记为 [Serializable] 的对象的深层副本,而无需编写任何特殊代码。

Here are two methods that will use Binary Serialization.这里有两种使用二进制序列化的方法。 If you use these methods you simply call如果您使用这些方法,您只需调用

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;
}

对我来说最好的方法是这样的:

Dictionary<int, int> copy= new Dictionary<int, int>(yourListOrDictionary);

Binary Serialization method works fine but in my tests it showed to be 10x slower than a non-serialization implementation of clone.二进制序列化方法工作正常,但在我的测试中,它比克隆的非序列化实现慢 10 倍。 Tested it on Dictionary<string , List<double>>Dictionary<string , List<double>>

Try this if key/values are ICloneable:如果键/值是 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;
    }

In the case you have a Dictionary of "object" and object can be anything like (double, int, ... or ComplexClass):如果你有一个“对象”字典,对象可以是任何东西(double、int、...或ComplexClass):

Dictionary<string, object> dictSrc { get; set; }

public class ComplexClass : ICloneable
{
    
    private Point3D ...;
    private Vector3D ....;
    [...]

    public object Clone()
    {
        ComplexClass clone = new ComplexClass();
        clone = (ComplexClass)this.MemberwiseClone();
        return clone;
    }

}


dictSrc["toto"] = new ComplexClass()
dictSrc["tata"] = 12.3
...

dictDest = dictSrc.ToDictionary(entry => entry.Key,
                                entry => ((entry.Value is ICloneable) ? (entry.Value as ICloneable).Clone() : entry.Value) );


Here is some real "true deep copying" without knowing type with some recursive walk, good for the beginnig.这是一些真正的“真正的深度复制”,不知道带有一些递归遍历的类型,适合初学者。 It is good for nested types and almost any tricky type I think.它适用于嵌套类型和我认为的几乎任何棘手的类型。 I did not added nested arrays handling yet, but you can modify it by your choice.我还没有添加嵌套数组处理,但您可以根据自己的选择修改它。

Dictionary<string, Dictionary<string, dynamic>> buildInfoDict =
    new Dictionary<string, Dictionary<string, dynamic>>()
    {
        {"tag",new Dictionary<string,dynamic>(){
                 { "attrName", "tag"  },
                 { "isCss", "False"  },
               { "turnedOn","True" },
                 { "tag",null }
            } },
        {"id",new Dictionary<string,dynamic>(){
                 { "attrName", "id"  },
                 { "isCss", "False"  },
               { "turnedOn","True" },
                 { "id",null }
            } },
                {"width",new Dictionary<string,dynamic>(){
                 { "attrName", "width"  },
                 { "isCss", "True"  },
               { "turnedOn","True" },
                 { "width","20%" }
            } },
                {"height",new Dictionary<string,dynamic>(){
                 { "attrName", "height"  },
                 { "isCss", "True"  },
               { "turnedOn","True" },
                 { "height","20%" }
            } },
                {"text",new Dictionary<string,dynamic>(){
                 { "attrName", null  },
                 { "isCss", "False"  },
               { "turnedOn","True" },
                 { "text","" }
            } },
                {"href",new Dictionary<string,dynamic>(){
                 { "attrName", null  },
                 { "isCss", "False"  },
                 { "flags", "removeAttrIfTurnedOff"  },
               { "turnedOn","True" },
                 { "href","about:blank" }
            } }
    };

var cln=clone(buildInfoDict);

public static dynamic clone(dynamic obj)
{
    dynamic cloneObj = null;
    if (IsAssignableFrom(obj, typeof(IDictionary)))
    {
        cloneObj = Activator.CreateInstance(obj.GetType());
        foreach (var key in obj.Keys)
        {
            cloneObj[key] = clone(obj[key]);
        }

    }
    else if (IsNumber(obj) || obj.GetType() == typeof(string))
    {
        cloneObj = obj;
    }
    else
    {
        Debugger.Break();
    }
    return cloneObj;
}


public static bool IsAssignableFrom(this object obj, Type ObjType = null, Type ListType = null, bool HandleBaseTypes = false)
{
    if (ObjType == null)
    {
        ObjType = obj.GetType();
    }

    bool Res;

    do
    {
        Res = (ObjType.IsGenericType && ObjType.GetGenericTypeDefinition().IsAssignableFrom(ListType)) ||
            (ListType == null && ObjType.IsAssignableFrom(obj.GetType()));
        ObjType = ObjType.BaseType;
    } while ((!Res && ObjType != null) && HandleBaseTypes && ObjType != typeof(object));

    return Res;
}

public static bool IsNumber(this object value)
{
    return value is sbyte
            || value is byte
            || value is short
            || value is ushort
            || value is int
            || value is uint
            || value is long
            || value is ulong
            || value is float
            || value is double
            || value is decimal;
}

Here is another way to clone a dictionary, assuming you know to do the "right" thing as far as handling whatever is hiding behind the "T" (aka "object") in your specific circumstances.这是克隆字典的另一种方法,假设您知道在您的特定情况下处理隐藏在“T”(又名“对象”)后面的任何东西,做“正确”的事情。

internal static Dictionary<string, object> Clone(Dictionary<string, object> dictIn) 
    {
        Dictionary<string, object> dictOut = new Dictionary<string, object>();
    
        IDictionaryEnumerator enumMyDictionary = dictIn.GetEnumerator();
        while (enumMyDictionary.MoveNext())
        {
            string strKey = (string)enumMyDictionary.Key;
            object oValue = enumMyDictionary.Value;
            dictOut.Add(strKey, oValue);
        }
    
        return dictOut; 
    }

I would evaluate if T was a value or reference type.我会评估 T 是值类型还是引用类型。 In the case T was a value type I would use the constructor of Dictionary, and in the case when T was a reference type I would make sure T inherited from ICloneable.在 T 是值类型的情况下,我将使用 Dictionary 的构造函数,而在 T 是引用类型的情况下,我将确保 T 继承自 ICloneable。

It will give它会给

    private static IDictionary<string, T> Copy<T>(this IDictionary<string, T> dict)
         where T : ICloneable
    {
        if (typeof(T).IsValueType)
        {
            return new Dictionary<string, T>(dict);
        }
        else
        {
            var copy = new Dictionary<string, T>();
            foreach (var pair in dict)
            {
                copy[pair.Key] = pair.Value;
            }
            return copy;
        }
    }

Replying on old post however I found it useful to wrap it as follows:回复旧帖子,但我发现将其包装如下很有用:

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;
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM