web-dev-qa-db-ja.com

.NET汎用ディクショナリ<string、T>を複製/ディープコピーする最良の方法は何ですか?

基本的に..anyの提案のClone()を作成したい汎用辞書Dictionary<string, T>を持っています。

183
mikeymo

さて、.NET 2.0は答えます:

値のクローンを作成する必要がない場合は、既存のIDictionaryを取得するDictionaryへのコンストラクタオーバーロードを使用できます。 (比較者を既存の辞書の比較者として指定することもできます。)

do値を複製する必要がある場合、次のようなものを使用できます。

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

もちろん、TValue.Clone()が適切に深いクローンであることにも依存しています。

172
Jon Skeet

(注:クローンバージョンは潜在的に便利ですが、単純な浅いコピーの場合、他の投稿で言及したコンストラクターがより良いオプションです。)

コピーの深さ、および使用している.NETのバージョンを教えてください。 .NET 3.5を使用している場合、キーセレクターと要素セレクターの両方を指定するToDictionaryのLINQ呼び出しが最も簡単な方法だと思います。

たとえば、値が浅いクローンであることを気にしない場合:

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

TにICloneableを実装するように既に制約している場合:

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

(これらはテストされていませんが、動作するはずです。)

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

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

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

Cloneメソッドを呼び出すだけで、辞書を複製できます。もちろん、この実装では、辞書の値型がICloneableを実装する必要がありますが、それ以外の場合、一般的な実装はまったく実用的ではありません。

10
Compile This

常にシリアル化を使用できます。オブジェクトをシリアル化してから逆シリアル化できます。これにより、ディクショナリとその中のすべてのアイテムの詳細コピーが得られます。これで、特別なコードを記述することなく、[シリアル化可能]としてマークされたオブジェクトのディープコピーを作成できます。

以下は、バイナリシリアル化を使用する2つの方法です。これらのメソッドを使用する場合は、単に呼び出します

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;
}
5
Shaun Bowe

私にとって最良の方法はこれです:

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

バイナリシリアル化方法は正常に機能しますが、私のテストでは、クローンの非シリアル化実装よりも10倍遅いことが示されました。 Dictionary<string , List<double>>でテストしました

3
loty

これは私のためにうまくいきます

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

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

Tomer Wolbergがコメントで説明しているように、値の型が可変クラスの場合、これは機能しません。

3
BonifatiusK

古い投稿に返信することはできましたが、次のようにラップすると便利です。

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;
  }
}
0
Decaf Sux

キー/値が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;
    }
0
Arvind