.NET 일반 사전 <string, T>을 복제 / 딥 복사하는 가장 좋은 방법은 무엇입니까?


211

Dictionary<string, T>본질적으로 .. 모든 제안의 Clone ()을 만들고 싶은 일반 사전 이 있습니다.

답변:


184

좋아, .NET 2.0 답변 :

값을 복제 할 필요가 없으면 생성자 오버로드를 기존 IDictionary를 사용하는 Dictionary에 사용할 수 있습니다. 비교자를 기존 사전의 비교 자로 지정할 수도 있습니다.

당신이 경우 않는 값을 복제 할 필요가, 당신은 이런 식으로 뭔가를 사용할 수 있습니다 :

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()물론 깊게 깊은 클론 에 의존합니다 .


그래도 사전 값의 얕은 사본 만 수행한다고 생각합니다. 이 entry.Value값은 또 다른 [sub] 컬렉션 일 수 있습니다.
ChrisW

6
@ChrisW : 음, 그것은 각 값을 복제하도록 요구하고 있습니다 Clone(). 그것은 깊거나 얕은 지 여부에 달려 있습니다. 그 효과에 메모를 추가했습니다.
Jon Skeet

1
@SaeedGanji : 값을 복제 할 필요가 없다면 "기존 IDictionary를 사용하는 Dictionary에 생성자 오버로드 사용"은 괜찮으며 이미 내 대답에 있습니다. 값이 경우 않는 복제 할 필요가 다음에 연결 한 대답은 전혀 없습니다 도움을 않습니다.
Jon Skeet

1
@SaeedGanji : 예, 그래야합니다. (물론, 구조체에 변경 가능한 참조 유형에 대한 참조가 포함되어 있으면 여전히 문제가 될 수 있지만, 그렇지 않은 경우도 있습니다.)
Jon Skeet

1
@SaeedGanji : 그것은 다른 일이 무엇인지에 달려 있습니다. 다른 스레드가 원래 사전 에서만 읽는 다면 괜찮을 것이라고 생각합니다. 무엇이든 수정하는 경우 동시에 스레드가 발생하지 않도록 해당 스레드와 복제 스레드를 모두 잠 가야합니다. 사전을 사용할 때 스레드 안전을 원하면을 사용하십시오 ConcurrentDictionary.
Jon Skeet

210

(참고 : 복제 버전이 유용 할 수는 있지만 간단한 얕은 사본의 경우 다른 게시물에서 언급 한 생성자가 더 나은 옵션입니다.)

복사본을 얼마나 깊이 원하십니까? 그리고 사용중인 .NET 버전은 무엇입니까? .NET 3.5를 사용하는 경우 키와 요소 선택기를 모두 지정하는 ToDictionary에 대한 LINQ 호출이 가장 쉬운 방법이라고 생각합니다.

예를 들어, 값이 얕은 복제본이 아니라면

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

ICloneable을 구현하도록 T를 이미 제한 한 경우 :

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

(테스트되지는 않았지만 작동해야합니다.)


답변 Jon에게 감사합니다. 실제로 v2.0의 프레임 워크를 사용하고 있습니다.
mikeymo

이 문맥에서 "entry => entry.Key, entry => entry.Value"는 무엇입니까? 키와 가치를 어떻게 추가합니까? 그것은 나의 끝에 오류를 보여
Pratik

2
@Pratik : C # 3의 일부인 람다 식입니다.
Jon Skeet

2
기본적으로 LINQ의 ToDictionary는 비교자를 복사하지 않습니다. 다른 답변에서 비교자를 복사하는 것에 대해 언급했지만이 버전의 복제도 비교자를 통과해야한다고 생각합니다.
user420667

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

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

5
값의 포인터는 여전히 동일합니다. 사본의 값에 변경 사항을 적용하면 변경 사항이 사전 오브젝트에도 반영됩니다.
Fokko Driesprong

3
@FokkoDriesprong 아니, 그것은 단지 새로운 객체에 keyValuePairs를 복사합니다

17
이것은 확실히 잘 작동합니다-키와 값의 복제본을 만듭니다. 물론 이것은 값이 참조 유형이 아닌 경우에만 작동합니다. 값이 참조 유형 인 경우에는 키 사본을 얕은 사본으로 만 효과적으로 복사합니다.
Contango

1
@Contango 그래서이 경우 string 및 int는 참조 유형이 아니므로 제대로 작동합니까?
MonsterMMORPG

3
@ UğurAldanmaz 당신은 참조 된 객체에 대한 실제 변경을 테스트하는 것을 잊어 버렸고 복제 된 사전에서 값 포인터의 교체 만 테스트하는 것이 분명하지만 테스트 객체의 속성을 다음과 같이 변경하면 테스트가 실패합니다 : dotnetfiddle.net / xmPPKr
Jens

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

그런 다음 단순히 Clone메소드 를 호출하여 사전을 복제 할 수 있습니다 . 물론이 구현은 딕셔너리의 값 유형이 구현을 요구 ICloneable하지만, 그렇지 않으면 일반적인 구현은 전혀 실용적이지 않습니다.


8

이것은 나를 위해 잘 작동

 // 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가 설명했듯이 값 유형이 변경 가능한 클래스 인 경우 작동하지 않습니다.


1
이것은 진지하게 upvotes를 필요로한다! 그러나 원래 사전이 읽기 전용 인 경우에도 여전히 작동합니다. var newDict = readonlyDict.ToDictionary (kvp => kvp.Key, kvp => kvp.Value)
Stephan

2
값 유형이 변경 가능한 클래스 인 경우 작동하지 않습니다.
Tomer Wolberg

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

5

나에게 가장 좋은 방법은 다음과 같습니다.

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

3
Dictionary가 참조 유형이므로 값이 아닌 참조를 복사하는 것입니까? 즉, 한 값을 변경하면 다른 값도 변경됩니까?
Goku

3

이진 직렬화 방법은 잘 작동하지만 테스트에서 복제의 비 직렬화 구현보다 10 배 느립니다. 그것을 테스트Dictionary<string , List<double>>


당신은 전체 깊은 복사를 했습니까? 문자열과리스트 모두 딥 카피해야합니다. 이 느린 원인 직렬화 버전의 몇 가지 버그가있다 :에 방법이라고 대신 . 그런 다음 byte []에서 먼저 MemStream에 수동으로 복사되지만 생성자에게 제공 될 수 있습니다. ToBinary()Serialize()thisyourDictionaryFromBinary()
목성

1

그것이 Dictionary <string, string>을 딥 카피하려고 할 때 도움이되었습니다.

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

행운을 빕니다


.NET 4.6.1에서 잘 작동합니다. 이것은 업데이트 된 답변이어야합니다.
Tallal Kazmi

0

키 / 값이 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

그러나 오래된 게시물에 회신하면 다음과 같이 포장하는 것이 유용하다는 것을 알았습니다.

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;
  }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.