.NET जेनेरिक शब्दकोश <string, T> को क्लोन / डीप कॉपी करने का सबसे अच्छा तरीका क्या है?


211

मुझे एक सामान्य शब्दकोष मिला है, Dictionary<string, T>जिसे मैं अनिवार्य रूप से ..जिसके सुझावों का क्लोन () बनाना चाहूंगा।

जवाबों:


184

ठीक है, .NET 2.0 उत्तर:

यदि आपको मानों को क्लोन करने की आवश्यकता नहीं है, तो आप कंस्ट्रक्टर ओवरलोड का उपयोग उस शब्दकोश में कर सकते हैं जो एक मौजूदा IDEDIA लेता है। (आप तुलनाकर्ता को मौजूदा शब्दकोश के तुलनाकर्ता के रूप में भी निर्दिष्ट कर सकते हैं।)

यदि आपको मानों को क्लोन करने की आवश्यकता है, तो आप इस तरह से कुछ का उपयोग कर सकते हैं:

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मूल्य हो सकता है अभी तक एक और [उप] संग्रह।
क्रिस डब्ल्यूडब्ल्यू

6
@ क्रिस: अच्छी तरह से है कि प्रत्येक मूल्य को क्लोन करने के लिए कह रहा है - यह इस Clone()विधि पर निर्भर है कि यह गहरा है या उथला है। मैंने इस आशय में एक नोट जोड़ा है।
जॉन स्कीट

1
@SaeedGanji: यदि मानों को क्लोन करने की आवश्यकता नहीं है, तो "डिक्शनरी को ओवरलोड का उपयोग करें जो एक मौजूदा आईडी एन्क्रिप्शन लेता है" ठीक है, और पहले से ही मेरे जवाब में है। तो जो मान करते क्लोन किए जाने की जरूरत है, तो इस सवाल का जवाब आप जुड़े हैं नहीं मदद सब पर है।
जॉन स्कीट

1
@ सईदगंजी: यह ठीक होना चाहिए, हाँ। (बेशक, अगर संरचना में परस्पर संदर्भ प्रकारों के संदर्भ शामिल हैं, जो अभी भी एक समस्या हो सकती है ... लेकिन उम्मीद है कि ऐसा नहीं है।)
जॉन स्कीट

1
@ सईदगंजी: यह इस बात पर निर्भर करता है कि और क्या चल रहा है। यदि अन्य सूत्र केवल मूल शब्दकोश से पढ़ रहे हैं , तो मेरा मानना ​​है कि यह ठीक होना चाहिए। यदि कुछ भी इसे संशोधित कर रहा है, तो आपको एक ही समय में होने से बचने के लिए, उस धागे और क्लोनिंग धागे दोनों में लॉक करना होगा। यदि आप शब्दकोशों का उपयोग करते समय थ्रेड सुरक्षा चाहते हैं, तो उपयोग करें ConcurrentDictionary
जॉन स्कीट

210

(ध्यान दें: हालांकि क्लोनिंग संस्करण संभावित रूप से उपयोगी है, एक साधारण उथले प्रतिलिपि के लिए जो कंस्ट्रक्टर मैंने दूसरे पोस्ट में उल्लेख किया है वह एक बेहतर विकल्प है।)

आप चाहते हैं कि प्रतिलिपि कितनी गहरी हो, और आप .NET के किस संस्करण का उपयोग कर रहे हैं? मुझे संदेह है कि कुंजी और तत्व चयनकर्ता दोनों को निर्दिष्ट करने के लिए एक LINQ टूबॉर्डर को कॉल करता है, यदि आप .NET 3.5 पर जा रहे हैं तो यह सबसे आसान तरीका होगा।

उदाहरण के लिए, यदि आपको उथले क्लोन होने का कोई मूल्य नहीं है:

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

यदि आप पहले से ही IC को लागू करने के लिए T को विवश कर चुके हैं:

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

(वे अनछुए हैं, लेकिन काम करना चाहिए।)


उत्तर जॉन के लिए धन्यवाद। मैं वास्तव में फ्रेमवर्क के v2.0 का उपयोग कर रहा हूं।
माइकमाओ

इस संदर्भ में "एंट्री => एंट्री.के, एंट्री => एंट्री.वैल्यू" क्या है। मैं कैसे कुंजी और मूल्य जोड़ूंगा यह मेरे अंत में एक त्रुटि दिखा रहा है
प्रतीक

2
@Pratik: वे लैंबडा एक्सप्रेशन हैं - C # 3. का हिस्सा
जॉन स्कीट

2
डिफ़ॉल्ट रूप से LINQ के टोडर तुलना की नकल नहीं करता है। आपने अपने दूसरे उत्तर में तुलना करने वाले की नकल करने का उल्लेख किया है, लेकिन मैं सोच रहा हूं कि क्लोनिंग के इस संस्करण को भी तुलनाकर्ता को पास करना चाहिए।
234 बजे user420667

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

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

5
मूल्यों के संकेत अभी भी समान हैं, यदि आप प्रतिलिपि में मूल्यों में परिवर्तन लागू करते हैं, तो परिवर्तन भी शब्दकोश वस्तु में दिखाई देंगे।
फ़ोकको ड्रिस्प्रॉन्ग

3
@FokkoDriesprong नहीं यह अभ्यस्त, यह सिर्फ नए ऑब्जेक्ट में keyValuePairs कॉपी करता है

17
यह निश्चित रूप से अच्छी तरह से काम करता है - यह कुंजी और मूल्य दोनों का एक क्लोन बनाता है। बेशक, यह केवल तभी काम करता है जब मान एक संदर्भ प्रकार नहीं है, यदि मान एक संदर्भ प्रकार है तो यह प्रभावी रूप से केवल एक उथले प्रति के रूप में कुंजियों की एक प्रति लेता है।
कंटैंगो

1
@Contango तो इस मामले में चूंकि स्ट्रिंग और int एक संदर्भ प्रकार नहीं हैं यह सही काम करेगा?
मॉन्स्टरमोरपीजी

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

जैसा कि टॉमर वोल्बर्ग टिप्पणियों में वर्णन करते हैं, यह काम नहीं करता है यदि मूल्य प्रकार एक उत्परिवर्ती वर्ग है।


1
यह गंभीरता से upvotes की जरूरत है! यदि मूल शब्दकोश आसानी से पढ़ा जाता है, तो भी, यह अभी भी काम करेगा: var newDict = readonlyDict.ToDictionary (kvp => kvp.Key, kvp => kvp.Value)
Stephan Ryer

2
यदि मान प्रकार एक उत्परिवर्ती वर्ग है
तोमर वोल्बर्ग

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
यह सिर्फ संदर्भ की नकल नहीं है और न ही मूल्यों क्योंकि शब्दकोश एक संदर्भ प्रकार है? इसका मतलब है कि यदि आप एक में मूल्यों को बदलते हैं तो यह दूसरे में मूल्य बदल देगा?
गोकू

3

बाइनरी सीरियलाइज़ेशन विधि ठीक काम करती है, लेकिन मेरे परीक्षणों में यह क्लोन के गैर-क्रमिकीकरण कार्यान्वयन की तुलना में 10 गुना धीमा है। इस पर परीक्षण कियाDictionary<string , List<double>>


क्या आप सुनिश्चित हैं कि आपने पूरी गहरी कॉपी की है? स्ट्रिंग्स और लिस्ट दोनों को गहराई से कॉपी करने की आवश्यकता है। वहाँ भी यह धीमी गति से होने के लिए पैदा कर रहा क्रमबद्धता संस्करण में कुछ कीड़े हैं: में विधि के साथ कहा जाता है के बजाय । फिर बाइट में [] पहले मेमस्ट्र्रीम में मैन्युअल रूप से कॉपी किया जाता है लेकिन इसे सिर्फ इसके कंस्ट्रक्टर को सप्लाई किया जा सकता है। ToBinary()Serialize()thisyourDictionaryFromBinary()
बृहस्पति

1

जब मैंने एक डिक्शनरी <string, string> को कॉपी करने की कोशिश की थी, तो इससे मुझे मदद मिली

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

सौभाग्य


.NET 4.6.1 के लिए अच्छा काम करता है। यह अद्यतन उत्तर होना चाहिए।
तलाल काज़मी

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;
  }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.