c# - आप.NET(सी#विशेष रूप से) में किसी ऑब्जेक्ट की गहरी प्रति कैसे करते हैं?




serialization clone (8)

Kilhoffer के समाधान पर बिल्डिंग ...

सी # 3.0 के साथ आप एक विस्तार विधि बना सकते हैं:

public static class ExtensionMethods
{
    // Deep clone
    public static T DeepClone<T>(this T a)
    {
        using (MemoryStream stream = new MemoryStream())
        {
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream, a);
            stream.Position = 0;
            return (T) formatter.Deserialize(stream);
        }
    }
}

जो किसी भी वर्ग को बढ़ाता है जिसे एक डीपक्लोन विधि के साथ [सीरियलज़ेबल] के रूप में चिह्नित किया गया है

MyClass copy = obj.DeepClone();

इस प्रश्न का उत्तर यहां दिया गया है:

मुझे एक सच्ची गहरी प्रतिलिपि चाहिए। जावा में, यह आसान था, लेकिन आप इसे सी # में कैसे करते हैं?


आप एक गहरी प्रतिलिपि करने के लिए Nested MemberwiseClone का उपयोग कर सकते हैं। एक मूल्य संरचना की प्रतिलिपि बनाने के रूप में इसकी लगभग एक ही गति, और इसकी प्रतिबिंब (ए) प्रतिबिंब या (बी) क्रमिकरण (जैसा कि इस पृष्ठ पर अन्य उत्तरों में वर्णित है) से तीव्रता का क्रम है।

ध्यान दें कि यदि आप एक गहरी प्रतिलिपि के लिए नेस्टेड सदस्यwiseक्लोन का उपयोग करते हैं, तो आपको कक्षा में प्रत्येक नेस्टेड स्तर के लिए मैन्युअल रूप से एक शैलोकोपी लागू करना होगा, और एक दीपकोपी जो सभी को पूर्ण क्लोन बनाने के लिए शैलोकोपी विधियों को कॉल करता है। यह आसान है: कुल में केवल कुछ पंक्तियां, नीचे डेमो कोड देखें।

यहां सापेक्ष प्रदर्शन अंतर दिखाने वाले कोड का आउटपुट है (सीरियलाइजेशन के लिए गहरे घोंसले वाले सदस्यवॉपी बनाम 39.93 सेकेंड के लिए 4.77 सेकंड)। नेस्टेड MemberwiseCopy का उपयोग लगभग एक संरचना की प्रतिलिपि बनाने के रूप में तेज़ है, और एक संरचना की प्रतिलिपि सैद्धांतिक अधिकतम गति के करीब सुंदर धुन है। नेट सक्षम है।

    Demo of shallow and deep copy, using classes and MemberwiseClone:
      Create Bob
        Bob.Age=30, Bob.Purchase.Description=Lamborghini
      Clone Bob >> BobsSon
      Adjust BobsSon details
        BobsSon.Age=2, BobsSon.Purchase.Description=Toy car
      Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:
        Bob.Age=30, Bob.Purchase.Description=Lamborghini
      Elapsed time: 00:00:04.7795670,30000000
    Demo of shallow and deep copy, using structs and value copying:
      Create Bob
        Bob.Age=30, Bob.Purchase.Description=Lamborghini
      Clone Bob >> BobsSon
      Adjust BobsSon details:
        BobsSon.Age=2, BobsSon.Purchase.Description=Toy car
      Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:
        Bob.Age=30, Bob.Purchase.Description=Lamborghini
      Elapsed time: 00:00:01.0875454,30000000
    Demo of deep copy, using class and serialize/deserialize:
      Elapsed time: 00:00:39.9339425,30000000

MemberwiseCopy का उपयोग करके गहरी प्रतिलिपि कैसे करें, यह समझने के लिए, यहां डेमो प्रोजेक्ट है:

// Nested MemberwiseClone example. 
// Added to demo how to deep copy a reference class.
[Serializable] // Not required if using MemberwiseClone, only used for speed comparison using serialization.
public class Person
{
    public Person(int age, string description)
    {
        this.Age = age;
        this.Purchase.Description = description;
    }
    [Serializable] // Not required if using MemberwiseClone
    public class PurchaseType
    {
        public string Description;
        public PurchaseType ShallowCopy()
        {
            return (PurchaseType)this.MemberwiseClone();
        }
    }
    public PurchaseType Purchase = new PurchaseType();
    public int Age;
    // Add this if using nested MemberwiseClone.
    // This is a class, which is a reference type, so cloning is more difficult.
    public Person ShallowCopy()
    {
        return (Person)this.MemberwiseClone();
    }
    // Add this if using nested MemberwiseClone.
    // This is a class, which is a reference type, so cloning is more difficult.
    public Person DeepCopy()
    {
            // Clone the root ...
        Person other = (Person) this.MemberwiseClone();
            // ... then clone the nested class.
        other.Purchase = this.Purchase.ShallowCopy();
        return other;
    }
}
// Added to demo how to copy a value struct (this is easy - a deep copy happens by default)
public struct PersonStruct
{
    public PersonStruct(int age, string description)
    {
        this.Age = age;
        this.Purchase.Description = description;
    }
    public struct PurchaseType
    {
        public string Description;
    }
    public PurchaseType Purchase;
    public int Age;
    // This is a struct, which is a value type, so everything is a clone by default.
    public PersonStruct ShallowCopy()
    {
        return (PersonStruct)this;
    }
    // This is a struct, which is a value type, so everything is a clone by default.
    public PersonStruct DeepCopy()
    {
        return (PersonStruct)this;
    }
}
// Added only for a speed comparison.
public class MyDeepCopy
{
    public static T DeepCopy<T>(T obj)
    {
        object result = null;
        using (var ms = new MemoryStream())
        {
            var formatter = new BinaryFormatter();
            formatter.Serialize(ms, obj);
            ms.Position = 0;
            result = (T)formatter.Deserialize(ms);
            ms.Close();
        }
        return (T)result;
    }
}

फिर, डेमो को मुख्य से कॉल करें:

    void MyMain(string[] args)
    {
        {
            Console.Write("Demo of shallow and deep copy, using classes and MemberwiseCopy:\n");
            var Bob = new Person(30, "Lamborghini");
            Console.Write("  Create Bob\n");
            Console.Write("    Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);
            Console.Write("  Clone Bob >> BobsSon\n");
            var BobsSon = Bob.DeepCopy();
            Console.Write("  Adjust BobsSon details\n");
            BobsSon.Age = 2;
            BobsSon.Purchase.Description = "Toy car";
            Console.Write("    BobsSon.Age={0}, BobsSon.Purchase.Description={1}\n", BobsSon.Age, BobsSon.Purchase.Description);
            Console.Write("  Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:\n");
            Console.Write("    Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);
            Debug.Assert(Bob.Age == 30);
            Debug.Assert(Bob.Purchase.Description == "Lamborghini");
            var sw = new Stopwatch();
            sw.Start();
            int total = 0;
            for (int i = 0; i < 100000; i++)
            {
                var n = Bob.DeepCopy();
                total += n.Age;
            }
            Console.Write("  Elapsed time: {0},{1}\n", sw.Elapsed, total);
        }
        {               
            Console.Write("Demo of shallow and deep copy, using structs:\n");
            var Bob = new PersonStruct(30, "Lamborghini");
            Console.Write("  Create Bob\n");
            Console.Write("    Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);
            Console.Write("  Clone Bob >> BobsSon\n");
            var BobsSon = Bob.DeepCopy();
            Console.Write("  Adjust BobsSon details:\n");
            BobsSon.Age = 2;
            BobsSon.Purchase.Description = "Toy car";
            Console.Write("    BobsSon.Age={0}, BobsSon.Purchase.Description={1}\n", BobsSon.Age, BobsSon.Purchase.Description);
            Console.Write("  Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:\n");
            Console.Write("    Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);                
            Debug.Assert(Bob.Age == 30);
            Debug.Assert(Bob.Purchase.Description == "Lamborghini");
            var sw = new Stopwatch();
            sw.Start();
            int total = 0;
            for (int i = 0; i < 100000; i++)
            {
                var n = Bob.DeepCopy();
                total += n.Age;
            }
            Console.Write("  Elapsed time: {0},{1}\n", sw.Elapsed, total);
        }
        {
            Console.Write("Demo of deep copy, using class and serialize/deserialize:\n");
            int total = 0;
            var sw = new Stopwatch();
            sw.Start();
            var Bob = new Person(30, "Lamborghini");
            for (int i = 0; i < 100000; i++)
            {
                var BobsSon = MyDeepCopy.DeepCopy<Person>(Bob);
                total += BobsSon.Age;
            }
            Console.Write("  Elapsed time: {0},{1}\n", sw.Elapsed, total);
        }
        Console.ReadKey();
    }

दोबारा, ध्यान दें कि यदि आप एक गहरी प्रतिलिपि के लिए नेस्टेड सदस्यwiseक्लोन का उपयोग करते हैं, तो आपको कक्षा में प्रत्येक नेस्टेड स्तर के लिए मैन्युअल रूप से एक शैलोकोपी लागू करना होगा, और एक दीपकोपी जो सभी को पूर्ण क्लोन बनाने के लिए शैलोकोपी विधियों को कॉल करता है। यह आसान है: कुल में केवल कुछ पंक्तियां, उपरोक्त डेमो कोड देखें।

ध्यान दें कि जब किसी ऑब्जेक्ट को क्लोन करने की बात आती है, तो "संरचना" और "कक्षा" के बीच एक बड़ा अंतर होता है:

  • यदि आपके पास "संरचना" है, तो यह एक मान प्रकार है ताकि आप इसे कॉपी कर सकें, और सामग्री को क्लोन कर दिया जाएगा।
  • यदि आपके पास "कक्षा" है, तो यह एक संदर्भ प्रकार है, इसलिए यदि आप इसे कॉपी करते हैं, तो आप जो भी कर रहे हैं वह पॉइंटर को कॉपी कर रहा है। एक वास्तविक क्लोन बनाने के लिए, आपको और अधिक रचनात्मक होना चाहिए, और एक विधि का उपयोग करना है जो मूल वस्तु की एक और प्रतिलिपि स्मृति में बनाता है।
  • गलत तरीके से क्लोनिंग ऑब्जेक्ट्स बहुत कठिन-से-पिन-डाउन बग का कारण बन सकता है। उत्पादन कोड में, मैं जांच करने के लिए एक चेकसम लागू करता हूं कि ऑब्जेक्ट को ठीक से क्लोन किया गया है, और इसके किसी अन्य संदर्भ से दूषित नहीं हुआ है। रिलीज मोड में यह चेकसम बंद हो सकता है।
  • मुझे यह विधि काफी उपयोगी लगता है: अक्सर, आप केवल ऑब्जेक्ट के हिस्सों को क्लोन करना चाहते हैं, पूरी बात नहीं। यह किसी भी उपयोग के मामले के लिए भी जरूरी है जहां आप वस्तुओं को संशोधित कर रहे हैं, फिर संशोधित प्रतियों को कतार में खिला रहे हैं।

अद्यतन करें

एक गहरी प्रतिलिपि करने के लिए ऑब्जेक्ट ग्राफ़ के माध्यम से पुनरावर्ती चलने के लिए प्रतिबिंब का उपयोग करना संभवतः संभव है। डब्ल्यूसीएफ इस तकनीक का उपयोग किसी ऑब्जेक्ट को क्रमबद्ध करने के लिए करता है, जिसमें इसके सभी बच्चे शामिल हैं। यह चाल सभी बाल वस्तुओं को एक विशेषता के साथ एनोटेट करना है जो इसे खोजने योग्य बनाता है। हालांकि, आप कुछ प्रदर्शन लाभ खो सकते हैं।

अद्यतन करें

स्वतंत्र गति परीक्षण पर उद्धरण (नीचे टिप्पणियां देखें):

मैंने नील की धारावाहिक / deserialize विस्तार विधि, Contango के नेस्टेड सदस्यwise क्लोन, एलेक्स Burtsev की प्रतिबिंब-आधारित विस्तार विधि और ऑटोमैपर, प्रत्येक 1 मिलियन बार का उपयोग कर अपना खुद का स्पीड टेस्ट चलाया है। Serialize-deserialize धीमा था, 15.7 सेकंड ले रहा था। फिर ऑटोमैपर आया, 10.1 सेकंड ले रहा था। प्रतिबिंब-आधारित विधि बहुत तेज थी जो 2.4 सेकंड लेती थी। अब तक सबसे तेजी से Nested MemberwiseClone था, 0.1 सेकंड ले रहा था। इसे क्लोन करने के लिए प्रत्येक वर्ग में कोड जोड़ने की परेशानी बनाम प्रदर्शन के लिए नीचे आता है। यदि प्रदर्शन एलेक्स बुर्त्सेव की विधि के साथ कोई मुद्दा नहीं है। - साइमन टेवेसी


एमएसडीएन दस्तावेज संकेत देता है कि क्लोन को एक गहरी प्रतिलिपि करनी चाहिए, लेकिन यह कभी स्पष्ट रूप से नहीं कहा गया है:

आईसीएलनेबल इंटरफ़ेस में एक सदस्य, क्लोन शामिल है, जिसका उद्देश्य सदस्यवाइज़क्लोन द्वारा प्रदान किए गए क्लोनिंग का समर्थन करना है ... सदस्यवाईक्लोन विधि एक उथली प्रतिलिपि बनाता है ...

आप मेरी पोस्ट सहायक पा सकते हैं।

http://pragmaticcoding.com/index.php/cloning-objects-in-c/


मेरा मानना ​​है कि बाइनरीफॉर्मेटर दृष्टिकोण अपेक्षाकृत धीमा है (जो मुझे आश्चर्यचकित हुआ!)। यदि आप ProtoBuf की आवश्यकताओं को पूरा करते हैं तो आप कुछ ऑब्जेक्ट्स के लिए ProtoBuf .NET का उपयोग करने में सक्षम हो सकते हैं। प्रोटोबफ से प्रारंभ पृष्ठ ( http://code.google.com/p/protobuf-net/wiki/GettingStarted ) से:

समर्थित प्रकारों पर नोट्स:

कस्टम कक्षाएं जो:

  • डेटा अनुबंध के रूप में चिह्नित हैं
  • एक पैरामीटर रहित कन्स्ट्रक्टर है
  • सिल्वरलाइट के लिए: सार्वजनिक हैं
  • कई आम primitives, आदि
  • एकल आयाम सरणी: टी []
  • सूची <टी> / IList <टी>
  • शब्दकोश <TKey, TValue> / IDictionary <TKey, TValue>
  • कोई भी प्रकार जो IENumerable <T> लागू करता है और इसमें एक जोड़ें (टी) विधि है

कोड मानता है कि निर्वाचित सदस्यों के आसपास प्रकार परिवर्तनीय होंगे। तदनुसार, कस्टम structs समर्थित नहीं हैं, क्योंकि वे अपरिवर्तनीय होना चाहिए।

यदि आपकी कक्षा इन आवश्यकताओं को पूरा करती है तो आप कोशिश कर सकते हैं:

public static void deepCopy<T>(ref T object2Copy, ref T objectCopy)
{
    using (var stream = new MemoryStream())
    {
        Serializer.Serialize(stream, object2Copy);
        stream.Position = 0;
        objectCopy = Serializer.Deserialize<T>(stream);
    }
}

वास्तव में बहुत तेज़ है ...

संपादित करें:

यहां संशोधन के लिए कामकाजी कोड है (.NET 4.6 पर परीक्षण किया गया)। यह System.Xml.Serialization और System.IO का उपयोग करता है। धारावाहिक के रूप में कक्षाओं को चिह्नित करने की कोई ज़रूरत नहीं है।

public void DeepCopy<T>(ref T object2Copy, ref T objectCopy)
{
    using (var stream = new MemoryStream())
    {
        var serializer = new XS.XmlSerializer(typeof(T));

        serializer.Serialize(stream, object2Copy);
        stream.Position = 0;
        objectCopy = (T)serializer.Deserialize(stream);
    }
}

मैंने इसके लिए कुछ अलग दृष्टिकोण देखे हैं, लेकिन मैं एक सामान्य उपयोगिता विधि का उपयोग इस प्रकार करता हूं:

public static T DeepClone<T>(T obj)
{
 using (var ms = new MemoryStream())
 {
   var formatter = new BinaryFormatter();
   formatter.Serialize(ms, obj);
   ms.Position = 0;

   return (T) formatter.Deserialize(ms);
 }
}

टिप्पणियाँ:

  • इस काम के लिए आपकी कक्षा को [Serializable] के रूप में चिह्नित किया जाना चाहिए।
  • आपकी स्रोत फ़ाइल में निम्न कोड शामिल होना चाहिए:

    using System.Runtime.Serialization.Formatters.Binary;
    using System.IO;
    

मैंने रिकर्सिव "MemberwiseClone" पर आधारित एक गहरी ऑब्जेक्ट कॉपी एक्सटेंशन विधि लिखा है । यह तेज़ है (बाइनरीफॉर्मेटर से तीन गुना तेज ), और यह किसी भी वस्तु के साथ काम करता है। आपको डिफ़ॉल्ट कन्स्ट्रक्टर या क्रमिक गुणों की आवश्यकता नहीं है।


सबसे अच्छा तरीका है:

    public interface IDeepClonable<T> where T : class
    {
        T DeepClone();
    }

    public class MyObj : IDeepClonable<MyObj>
    {
        public MyObj Clone()
        {
            var myObj = new MyObj();
            myObj._field1 = _field1;//value type
            myObj._field2 = _field2;//value type
            myObj._field3 = _field3;//value type

            if (_child != null)
            {
                myObj._child = _child.DeepClone(); //reference type .DeepClone() that does the same
            }

            int len = _array.Length;
            myObj._array = new MyObj[len]; // array / collection
            for (int i = 0; i < len; i++)
            {
                myObj._array[i] = _array[i];
            }

            return myObj;
        }

        private bool _field1;
        public bool Field1
        {
            get { return _field1; }
            set { _field1 = value; }
        }

        private int _field2;
        public int Property2
        {
            get { return _field2; }
            set { _field2 = value; }
        }

        private string _field3;
        public string Property3
        {
            get { return _field3; }
            set { _field3 = value; }
        }

        private MyObj _child;
        private MyObj Child
        {
            get { return _child; }
            set { _child = value; }
        }

        private MyObj[] _array = new MyObj[4];
    }

    public static object CopyObject(object input)
    {
        if (input != null)
        {
            object result = Activator.CreateInstance(input.GetType());
            foreach (FieldInfo field in input.GetType().GetFields(Consts.AppConsts.FullBindingList))
            {
                if (field.FieldType.GetInterface("IList", false) == null)
                {
                    field.SetValue(result, field.GetValue(input));
                }
                else
                {
                    IList listObject = (IList)field.GetValue(result);
                    if (listObject != null)
                    {
                        foreach (object item in ((IList)field.GetValue(input)))
                        {
                            listObject.Add(CopyObject(item));
                        }
                    }
                }
            }
            return result;
        }
        else
        {
            return null;
        }
    }

इस तरह BinarySerialization तुलना में कुछ गुना तेज है और इसके लिए [Serializable] विशेषता की आवश्यकता नहीं है।







clone