Cách tốt nhất để sao chép / sao chép sâu một từ điển chung .NET <chuỗi, T> là gì?


211

Tôi đã có một từ điển chung Dictionary<string, T>mà về cơ bản tôi muốn tạo một bản sao () của .. rất nhiều đề xuất.

Câu trả lời:


184

Được rồi, câu trả lời .NET 2.0:

Nếu bạn không cần sao chép các giá trị, bạn có thể sử dụng quá tải hàm tạo cho Từ điển để có IDadata hiện có. (Bạn cũng có thể chỉ định bộ so sánh là bộ so sánh của từ điển hiện có.)

Nếu bạn làm cần phải sao chép các giá trị, bạn có thể sử dụng một cái gì đó như thế này:

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

Điều đó phụ thuộc vào TValue.Clone()việc là một bản sao sâu phù hợp là tốt, tất nhiên.


Tuy nhiên, tôi nghĩ rằng đó chỉ là một bản sao nông của các giá trị từ điển. Các entry.Valuegiá trị có thể được nêu ra một [sub] bộ sưu tập.
ChrisW

6
@ChrisW: Vâng, đó là yêu cầu mỗi giá trị được nhân bản - tùy thuộc vào Clone()phương pháp cho dù nó sâu hay nông. Tôi đã thêm một lưu ý cho hiệu ứng đó.
Jon Skeet

1
@SaeedGanji: Vâng, nếu các giá trị không cần được sao chép, "sử dụng quá tải hàm tạo cho Từ điển có IDadata hiện có" là tốt, và đã có trong câu trả lời của tôi. Nếu các giá trị làm cần được nhân bản, thì câu trả lời bạn đã liên kết với không giúp đỡ gì cả.
Jon Skeet

1
@SaeedGanji: Điều đó sẽ ổn thôi, vâng. (Tất nhiên, nếu các cấu trúc có chứa các tham chiếu đến các loại tham chiếu có thể thay đổi, thì đó vẫn có thể là một vấn đề ... nhưng hy vọng đó không phải là trường hợp.)
Jon Skeet

1
@SaeedGanji: Điều đó phụ thuộc vào những gì khác đang diễn ra. Nếu các chủ đề khác chỉ đọc từ từ điển gốc, thì tôi tin rằng nó sẽ ổn. Nếu bất cứ điều gì đang sửa đổi nó, bạn sẽ cần phải khóa cả luồng đó và luồng nhân bản, để tránh chúng xảy ra cùng một lúc. Nếu bạn muốn an toàn chủ đề khi sử dụng từ điển, sử dụng ConcurrentDictionary.
Jon Skeet

210

(Lưu ý: mặc dù phiên bản nhân bản có khả năng hữu ích, đối với một bản sao nông đơn giản, hàm tạo mà tôi đề cập trong bài đăng khác là một lựa chọn tốt hơn.)

Bạn muốn bản sao sâu đến mức nào và bạn đang sử dụng phiên bản .NET nào? Tôi nghi ngờ rằng một cuộc gọi LINQ tới ToDipedia, chỉ định cả bộ chọn khóa và phần tử, sẽ là cách dễ nhất để sử dụng nếu bạn đang sử dụng .NET 3.5.

Ví dụ: nếu bạn không nhớ giá trị là một bản sao nông:

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

Nếu bạn đã hạn chế T để triển khai IClonizable:

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

(Những người chưa được kiểm tra, nhưng nên làm việc.)


Cảm ơn câu trả lời Jon. Tôi thực sự đang sử dụng v2.0 của khung.
mikeymo

"Entry => entry.Key, entry => entry.Value" trong ngữ cảnh này là gì. Làm thế nào tôi sẽ thêm khóa và giá trị. Nó hiển thị một lỗi ở cuối của tôi
Pratik

2
@Pratik: Chúng là các biểu thức lambda - một phần của C # 3.
Jon Skeet

2
Theo mặc định, LIND's ToDipedia không sao chép bộ so sánh. Bạn đã đề cập đến việc sao chép bộ so sánh trong câu trả lời khác của bạn, nhưng tôi nghĩ phiên bản nhân bản này cũng sẽ vượt qua bộ so sánh.
user420667

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

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

5
Con trỏ của các giá trị vẫn giống nhau, nếu bạn áp dụng các thay đổi cho các giá trị trong bản sao, các thay đổi cũng sẽ được phản ánh trong đối tượng từ điển.
Fokko Driesprong

3
@FokkoDriesprong không có gì, nó chỉ sao chép keyValuePairs trong đối tượng mới

17
Điều này chắc chắn hoạt động tốt - nó tạo ra một bản sao của cả khóa và giá trị. Tất nhiên, điều này chỉ hoạt động nếu giá trị KHÔNG phải là loại tham chiếu, nếu giá trị là loại tham chiếu thì nó thực sự chỉ lấy một bản sao của các khóa làm bản sao nông.
Contango

1
@Contango vì vậy trong trường hợp này vì chuỗi và int KHÔNG phải là kiểu tham chiếu nên nó sẽ hoạt động phải không?
MonsterMMORPG

3
@ UğurAldanmaz bạn quên kiểm tra một thay đổi thực tế cho một đối tượng được tham chiếu, bạn chỉ kiểm tra thay thế các con trỏ giá trị trong từ điển nhân bản rõ ràng hoạt động, nhưng các thử nghiệm của bạn sẽ thất bại nếu bạn chỉ thay đổi các thuộc tính trên các đối tượng thử nghiệm của mình, như vậy: dotnetfiddle.net / xmPPKr
Jens

10

Đối với .NET 2.0, bạn có thể triển khai một lớp kế thừa Dictionaryvà thực hiện 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;
    }
}

Sau đó, bạn có thể sao chép từ điển chỉ bằng cách gọi Clonephương thức. Tất nhiên việc thực hiện này đòi hỏi loại giá trị của từ điển thực hiện ICloneable, nhưng nếu không thì việc triển khai chung không thực tế chút nào.


8

Cái này làm việc tốt cho tôi

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

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

Như Tomer Wolberg mô tả trong các bình luận, điều này không hoạt động nếu loại giá trị là một lớp có thể thay đổi.


1
Điều này thực sự cần upvotes! Tuy nhiên, nếu từ điển gốc chỉ đọc, thì nó vẫn hoạt động: var newDict = readonlyDict.ToDixi (kvp => kvp.Key, kvp => kvp.Value)
Stephan Ryer

2
Điều đó không hiệu quả nếu loại giá trị là một lớp có thể thay đổi
Tomer Wolberg

5

Bạn luôn có thể sử dụng serialization. Bạn có thể tuần tự hóa đối tượng sau đó giải tuần tự hóa nó. Điều đó sẽ cung cấp cho bạn một bản sao sâu của Từ điển và tất cả các mục bên trong nó. Bây giờ bạn có thể tạo một bản sao sâu của bất kỳ đối tượng nào được đánh dấu là [Nối tiếp] mà không cần viết bất kỳ mã đặc biệt nào.

Đây là hai phương pháp sẽ sử dụng Binary serialization. Nếu bạn sử dụng các phương pháp này, bạn chỉ cần gọi

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

Cách tốt nhất cho tôi là thế này:

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

3
Không phải đây chỉ là sao chép tham chiếu chứ không phải các giá trị vì Từ điển là loại tham chiếu? điều đó có nghĩa là nếu bạn thay đổi các giá trị trong một thì nó sẽ thay đổi giá trị trong cái kia?
Goku

3

Phương pháp tuần tự hóa nhị phân hoạt động tốt nhưng trong các thử nghiệm của tôi, nó cho thấy chậm hơn gấp 10 lần so với thực hiện không tuần tự hóa bản sao. Đã thử nó trênDictionary<string , List<double>>


Bạn có chắc chắn rằng bạn đã làm một bản sao đầy đủ sâu? Cả chuỗi và Danh sách cần được sao chép sâu. Ngoài ra còn có một số lỗi trong phiên bản serialization làm cho nó chậm: trong ToBinary()các Serialize()phương pháp được gọi với thisthay vì yourDictionary. Sau đó, trong FromBinary()byte [] đầu tiên được sao chép thủ công vào MemStream nhưng nó chỉ có thể được cung cấp cho hàm tạo của nó.
Sao Mộc

1

Đó là điều đã giúp tôi, khi tôi đang cố gắng sao chép sâu một từ điển <chuỗi, chuỗi>

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

Chúc may mắn


Hoạt động tốt cho .NET 4.6.1. Đây phải là câu trả lời cập nhật.
Tallal Kazmi

0

Hãy thử điều này nếu khóa / giá trị có thể IClonizable:

    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

Trả lời trên bài viết cũ tuy nhiên tôi thấy nó hữu ích để bọc nó như sau:

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;
  }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.