गहरी क्लोनिंग ऑब्जेक्ट्स


2226

मैं कुछ ऐसा करना चाहता हूं:

MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = myObj.Clone();

और फिर नई वस्तु में परिवर्तन करें जो मूल वस्तु में परिलक्षित नहीं होते हैं।

मुझे अक्सर इस कार्यक्षमता की आवश्यकता नहीं होती है, इसलिए जब यह आवश्यक हो, मैंने एक नई वस्तु बनाने और फिर प्रत्येक संपत्ति को व्यक्तिगत रूप से कॉपी करने का सहारा लिया है, लेकिन यह हमेशा मुझे इस भावना के साथ छोड़ देता है कि हैंडलिंग का एक बेहतर या अधिक सुरुचिपूर्ण तरीका है स्थिति।

मैं किसी ऑब्जेक्ट को कैसे क्लोन या डीप कॉपी कर सकता हूं ताकि क्लोन किए गए ऑब्जेक्ट को बिना किसी बदलाव के मूल ऑब्जेक्ट में परिलक्षित किया जा सके?


81
उपयोगी हो सकता है: "क्यों एक वस्तु की नकल करना एक भयानक काम है?" agiledeveloper.com/articles/cloning072002.htm
पेड्रो77


18
आपको AutoMapper
Daniel Little

3
आपका समाधान कहीं अधिक जटिल है, मैं इसे पढ़कर खो गया ... हेहेहे। मैं एक डीपक्लोन इंटरफ़ेस का उपयोग कर रहा हूं। सार्वजनिक इंटरफ़ेस IDeepCloneable <T> {T DeepClone (); }
पेड्रो77

1
@ पेड्रो77 - हालांकि, दिलचस्प बात यह है कि यह लेख cloneकक्षा पर एक विधि बनाने के लिए कह रहा है , फिर क्या इसे एक आंतरिक, निजी निर्माणकर्ता कहते हैं जो पास हो जाता है this। इसलिए नकल करना बहुत ही भयानक है, लेकिन ध्यान से कॉपी करना (और लेख निश्चित रूप से पढ़ने लायक है) नहीं है। ; ^)
रफिन

जवाबों:


1715

जब भी मानक अभ्यास ICloneableइंटरफ़ेस को लागू करने के लिए है ( यहाँ वर्णित है , तो मैं इसे पुनर्जन्म नहीं करूँगा), यहाँ एक अच्छी गहरी क्लोन ऑब्जेक्ट कोपियर है जिसे मैंने कोड प्रोजेक्ट पर पाया है थोड़ी देर पहले था और इसे हमारे सामान में शामिल किया था।

जैसा कि कहीं और उल्लेख किया गया है, इसके लिए आपकी वस्तुओं को क्रमबद्ध होना आवश्यक है।

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

/// <summary>
/// Reference Article http://www.codeproject.com/KB/tips/SerializedObjectCloner.aspx
/// Provides a method for performing a deep copy of an object.
/// Binary Serialization is used to perform the copy.
/// </summary>
public static class ObjectCopier
{
    /// <summary>
    /// Perform a deep Copy of the object.
    /// </summary>
    /// <typeparam name="T">The type of object being copied.</typeparam>
    /// <param name="source">The object instance to copy.</param>
    /// <returns>The copied object.</returns>
    public static T Clone<T>(T source)
    {
        if (!typeof(T).IsSerializable)
        {
            throw new ArgumentException("The type must be serializable.", nameof(source));
        }

        // Don't serialize a null object, simply return the default for that object
        if (Object.ReferenceEquals(source, null))
        {
            return default(T);
        }

        IFormatter formatter = new BinaryFormatter();
        Stream stream = new MemoryStream();
        using (stream)
        {
            formatter.Serialize(stream, source);
            stream.Seek(0, SeekOrigin.Begin);
            return (T)formatter.Deserialize(stream);
        }
    }
}

विचार यह है कि यह आपकी वस्तु को क्रमबद्ध करता है और फिर इसे एक नई वस्तु में बदल देता है। लाभ यह है कि जब कोई वस्तु बहुत जटिल हो जाती है तो आपको हर चीज की क्लोनिंग के बारे में चिंता करने की आवश्यकता नहीं है।

और विस्तार विधियों के उपयोग के साथ (मूल रूप से संदर्भित स्रोत से भी):

यदि आप C # 3.0 की नई एक्सटेंशन विधियों का उपयोग करना पसंद करते हैं , तो निम्न हस्ताक्षर करने के लिए विधि बदलें:

public static T Clone<T>(this T source)
{
   //...
}

अब विधि कॉल बस बन जाता है objectBeingCloned.Clone();

EDIT (10 जनवरी 2015) सोचा था कि मैं इसे फिर से देखूंगा, यह उल्लेख करने के लिए कि मैंने हाल ही में (न्यूटनसॉफ्ट) Json का उपयोग करना शुरू किया है, यह हल्का होना चाहिए , और [Serializable] टैग के ओवरहेड से बचा जाता है। ( NB @atconway ने टिप्पणियों में बताया है कि निजी सदस्य JSON पद्धति का उपयोग करके क्लोन नहीं किए जाते हैं)

/// <summary>
/// Perform a deep Copy of the object, using Json as a serialisation method. NOTE: Private members are not cloned using this method.
/// </summary>
/// <typeparam name="T">The type of object being copied.</typeparam>
/// <param name="source">The object instance to copy.</param>
/// <returns>The copied object.</returns>
public static T CloneJson<T>(this T source)
{            
    // Don't serialize a null object, simply return the default for that object
    if (Object.ReferenceEquals(source, null))
    {
        return default(T);
    }

    // initialize inner objects individually
    // for example in default constructor some list property initialized with some values,
    // but in 'source' these items are cleaned -
    // without ObjectCreationHandling.Replace default constructor values will be added to result
    var deserializeSettings = new JsonSerializerSettings {ObjectCreationHandling = ObjectCreationHandling.Replace};

    return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source), deserializeSettings);
}

24
stackoverflow.com/questions/78536/cloning-objects-in-c/… के पास ऊपर दिए गए कोड का लिंक है [और दो अन्य ऐसे कार्यान्वयन को संदर्भित करता है, जिनमें से एक मेरे संदर्भ में अधिक उपयुक्त है]
रुबेन बार्टेलिंक

102
Serialization / deserialization में महत्वपूर्ण ओवरहेड शामिल है जो आवश्यक नहीं है। ICloneable इंटरफ़ेस और .Memberwise () C # में क्लोन तरीके देखें।

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

16
@Amir: वास्तव में, नहीं: typeof(T).IsSerializableयह भी सच है कि क्या प्रकार को [Serializable]विशेषता के साथ चिह्नित किया गया है। यह ISerializableइंटरफ़ेस को लागू करने की जरूरत नहीं है ।
डैनियल गेहरिगर

11
मैंने सोचा कि मैं उल्लेख करूंगा कि यह विधि उपयोगी है, और मैंने इसे स्वयं कई बार उपयोग किया है, यह मीडियम ट्रस्ट के साथ बिल्कुल भी संगत नहीं है - इसलिए देखें कि क्या आप ऐसा कोड लिख रहे हैं जिसे अनुकूलता की आवश्यकता है। बाइनरीफ़ॉर्मेटर निजी क्षेत्रों तक पहुंच प्राप्त करता है और इस प्रकार आंशिक विश्वास वातावरण के लिए डिफ़ॉल्ट अनुमतियों में काम नहीं कर सकता है। आप एक और धारावाहिक बनाने की कोशिश कर सकते हैं, लेकिन सुनिश्चित करें कि आपका कॉलर जानता है कि क्लोन सही नहीं हो सकता है यदि आने वाली वस्तु निजी क्षेत्रों पर निर्भर करती है।
एलेक्स नोरक्लिफ

298

मैं ज्यादातर आदिम और सूचियों की बहुत ही साधारण वस्तुओं के लिए एक क्लोनर चाहता था। यदि आपकी वस्तु JSON सीरियल करने योग्य नहीं है, तो यह विधि ट्रिक करेगी। इसके लिए क्लोन वर्ग पर इंटरफेस की कोई संशोधन या कार्यान्वयन की आवश्यकता नहीं है, सिर्फ JSON.NET जैसे JSON धारावाहिक।

public static T Clone<T>(T source)
{
    var serialized = JsonConvert.SerializeObject(source);
    return JsonConvert.DeserializeObject<T>(serialized);
}

इसके अलावा, आप इस एक्सटेंशन विधि का उपयोग कर सकते हैं

public static class SystemExtension
{
    public static T Clone<T>(this T source)
    {
        var serialized = JsonConvert.SerializeObject(source);
        return JsonConvert.DeserializeObject<T>(serialized);
    }
}

13
solutiojn बाइनरीफ़ॉर्मेटर समाधान से भी तेज़ है। .NET
सीरियललाइज़ेशन

3
इसके लिए धन्यवाद। मैं अनिवार्य रूप से BSON धारावाहिक के साथ वही काम करने में सक्षम था जो C # के लिए MongoDB ड्राइवर के साथ जहाज करता था।
मार्क ईवर

3
मेरे लिए यह सबसे अच्छा तरीका है, हालांकि, मैं उपयोग करता हूं Newtonsoft.Json.JsonConvertलेकिन यह समान है
पियरे

1
इसके लिए क्लोन करने के लिए ऑब्जेक्ट को काम करने योग्य बनाने की जरूरत है जैसा कि पहले ही उल्लेख किया गया है - इसका मतलब यह भी है कि इसके लिए परिपत्र निर्भरता नहीं हो सकती है
radomeit

2
मुझे लगता है कि यह सबसे अच्छा समाधान है क्योंकि कार्यान्वयन को अधिकांश प्रोग्रामिंग भाषाओं पर लागू किया जा सकता है।
mr5

178

ICloneable का उपयोग नहीं करने का कारण यह नहीं है क्योंकि इसमें जेनेरिक इंटरफ़ेस नहीं है। इसका उपयोग नहीं करने का कारण यह है क्योंकि यह अस्पष्ट है । यह स्पष्ट नहीं करता है कि आपको उथला या गहरा प्रति मिल रहा है या नहीं; यह कार्यान्वयनकर्ता तक है।

हां, MemberwiseCloneउथली प्रतिलिपि बनाता है, लेकिन इसके विपरीत MemberwiseCloneनहीं है Clone; यह होगा, शायद,DeepClone , जो मौजूद नहीं है। जब आप इसके ICloneable इंटरफ़ेस के माध्यम से किसी ऑब्जेक्ट का उपयोग करते हैं, तो आप यह नहीं जान सकते हैं कि अंतर्निहित ऑब्जेक्ट का क्लोनिंग किस प्रकार का प्रदर्शन करता है। (और एक्सएमएल टिप्पणियां इसे स्पष्ट नहीं करेंगी, क्योंकि आपको ऑब्जेक्ट के क्लोन विधि के बजाय इंटरफ़ेस टिप्पणियां मिलेंगी।)

जो मैं आमतौर पर करता हूं, वह बस एक Copyविधि है जो वास्तव में वही करता है जो मैं चाहता हूं।


मैं स्पष्ट नहीं हूं कि ICloneable को अस्पष्ट क्यों माना जाता है। डिक्शनरी (टी, यू) जैसे प्रकार को देखते हुए, मैं उम्मीद करूंगा कि ICloneable.Clone को जो कुछ भी करना चाहिए वह गहरी और उथले नकल करना आवश्यक है ताकि नया शब्दकोश एक स्वतंत्र शब्दकोश हो जिसमें एक ही टी और यू (संरचना सामग्री) शामिल हो, और / या वस्तु संदर्भ) मूल के रूप में। अस्पष्टता कहाँ है? यह सुनिश्चित करने के लिए, एक सामान्य ICloneable (T का), जिसे ISelf (T का T) विरासत में मिला, जिसमें एक "स्व" विधि शामिल थी, बहुत बेहतर होगी, लेकिन मुझे गहरी बनाम उथली क्लोनिंग पर अस्पष्टता नहीं दिखती।
सुपरकैट

31
आपका उदाहरण समस्या को दर्शाता है। मान लीजिए कि आपके पास एक शब्दकोश है <string, customer>। क्या क्लोन किए गए शब्दकोश में उन ग्राहक वस्तुओं के मूल, या प्रतियों के समान ग्राहक वस्तुएं होनी चाहिए ? किसी एक के लिए उचित उपयोग के मामले हैं। लेकिन ICloneable स्पष्ट नहीं करता है कि आपको कौन सा मिलेगा। इसलिए यह उपयोगी नहीं है।
रेयान लुंडी

@Kyralessa Microsoft MSDN आलेख वास्तव में यह जानने की बहुत समस्या बताता है कि क्या आप एक गहरी या उथली प्रति का अनुरोध कर रहे हैं।
क्रश

डुप्लिकेट stackoverflow.com/questions/129389// से उत्तर पुनरावर्ती सदस्यता के आधार पर कॉपी एक्सटेंशन का वर्णन करता है
माइकल फ्रीजिम

123

यहाँ से जुड़े कई विकल्पों, और इस समस्या के संभावित समाधानों के बारे में बहुत कुछ पढ़ने के बाद, मेरा मानना ​​है कि सभी विकल्पों को संक्षेप में Ian P के लिंक पर अच्छी तरह से संक्षेपित किया गया है (अन्य सभी विकल्प उन के रूपांतर हैं) और सबसे अच्छा समाधान इसके द्वारा प्रदान किया गया है पेड्रो77 प्रश्न टिप्पणियों पर लिंक है

तो मैं यहां उन 2 संदर्भों के प्रासंगिक भागों की प्रतिलिपि बनाऊंगा। इस तरह हम कर सकते हैं:

सी तेज में वस्तुओं के क्लोनिंग के लिए सबसे अच्छी बात!

और सबसे पहले, वे हमारे सभी विकल्प हैं:

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

मैं ICloneable (अर्थात मैन्युअल रूप से) का चयन क्यों करता हूं

श्री वेंकट सुब्रमण्यम (यहाँ निरर्थक लिंक) बहुत विस्तार से बताते हैं कि क्यों

एक उदाहरण के इर्द-गिर्द उसके सभी लेख हलकों में हैं, जो 3 वस्तुओं का उपयोग करके अधिकांश मामलों के लिए लागू होने की कोशिश करता है: व्यक्ति , मस्तिष्क और शहर । हम एक व्यक्ति का क्लोन बनाना चाहते हैं, जिसका अपना मस्तिष्क होगा लेकिन एक ही शहर होगा। आप या तो उपरोक्त सभी तरीकों में से किसी भी समस्या को चित्र में ला सकते हैं या लेख को पढ़ सकते हैं।

यह उनके निष्कर्ष का मेरा थोड़ा संशोधित संस्करण है:

Newकक्षा के नाम के बाद निर्दिष्ट करके किसी ऑब्जेक्ट को कॉपी करना अक्सर कोड की ओर जाता है जो एक्स्टेंसिबल नहीं है। क्लोन का उपयोग करना, प्रोटोटाइप पैटर्न का अनुप्रयोग, इसे प्राप्त करने का एक बेहतर तरीका है। हालाँकि, क्लोन का उपयोग करना क्योंकि यह C # (और Java) में दिया गया है और साथ ही साथ काफी समस्याग्रस्त भी हो सकता है। एक संरक्षित (गैर-सार्वजनिक) प्रतिलिपि निर्माता प्रदान करना और क्लोन विधि से इसे लागू करना बेहतर है। यह हमें एक वर्ग के एक उदाहरण के लिए एक वस्तु बनाने के कार्य को सौंपने की क्षमता देता है, इस प्रकार से सुरक्षा प्रदान करता है और साथ ही, संरक्षित प्रतिलिपि निर्माता का उपयोग करके वस्तुओं को सुरक्षित रूप से बनाता है।

उम्मीद है कि यह कार्यान्वयन चीजों को स्पष्ट कर सकता है:

public class Person : ICloneable
{
    private final Brain brain; // brain is final since I do not want 
                // any transplant on it once created!
    private int age;
    public Person(Brain aBrain, int theAge)
    {
        brain = aBrain; 
        age = theAge;
    }
    protected Person(Person another)
    {
        Brain refBrain = null;
        try
        {
            refBrain = (Brain) another.brain.clone();
            // You can set the brain in the constructor
        }
        catch(CloneNotSupportedException e) {}
        brain = refBrain;
        age = another.age;
    }
    public String toString()
    {
        return "This is person with " + brain;
        // Not meant to sound rude as it reads!
    }
    public Object clone()
    {
        return new Person(this);
    }
    
}

अब व्यक्ति से एक वर्ग प्राप्त करने पर विचार करें।

public class SkilledPerson extends Person
{
    private String theSkills;
    public SkilledPerson(Brain aBrain, int theAge, String skills)
    {
        super(aBrain, theAge);
        theSkills = skills;
    }
    protected SkilledPerson(SkilledPerson another)
    {
        super(another);
        theSkills = another.theSkills;
    }

    public Object clone()
    {
        return new SkilledPerson(this);
    }
    public String toString()
    {
        return "SkilledPerson: " + super.toString();
    }
}

आप निम्नलिखित कोड चलाने की कोशिश कर सकते हैं:

public class User
{
    public static void play(Person p)
    {
        Person another = (Person) p.clone();
        System.out.println(p);
        System.out.println(another);
    }
    public static void main(String[] args)
    {
        Person sam = new Person(new Brain(), 1);
        play(sam);
        SkilledPerson bob = new SkilledPerson(new SmarterBrain(), 1, "Writer");
        play(bob);
    }
}

उत्पादित उत्पादन होगा:

This is person with Brain@1fcc69
This is person with Brain@253498
SkilledPerson: This is person with SmarterBrain@1fef6f
SkilledPerson: This is person with SmarterBrain@209f4e

इस बात पर गौर करें कि यदि हम वस्तुओं की संख्या की गणना करते हैं, तो जैसा कि यहां लागू किया गया है, वस्तुओं की संख्या की सही गणना रखेंगे।


6
एमएस ICloneableसार्वजनिक सदस्यों के लिए उपयोग नहीं करने की सिफारिश करता है । "क्योंकि क्लोन के कॉलर एक पूर्वानुमानित क्लोनिंग ऑपरेशन करने वाली विधि पर निर्भर नहीं हो सकते, हम अनुशंसा करते हैं कि ICloneable सार्वजनिक APIs में लागू नहीं की जाए।" msdn.microsoft.com/en-us/library/… हालांकि, आपके लिंक किए गए लेख में वेंकट सुब्रमण्यम द्वारा दिए गए स्पष्टीकरण के आधार पर, मुझे लगता है कि यह इस स्थिति में उपयोग करने के लिए समझ में आता है जब तक कि ICloneable वस्तुओं के रचनाकारों का गहरा संबंध है यह समझने की कि कौन सी संपत्तियाँ गहरी बनाम उथली प्रतियाँ होनी चाहिए (अर्थात गहरी प्रति मस्तिष्क, उथली प्रतिलिपि सिटी)
बेटेक

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

आप CGbR क्लोन जेनरेटर का उपयोग कर सकते हैं और कोड लिखे बिना मैन्युअल रूप से एक समान परिणाम प्राप्त कर सकते हैं ।
विषाक्त

इंटरमीडिएट भाषा कार्यान्वयन उपयोगी है
माइकल फ्रीजिम


84

मैं एक क्लोन के लिए एक कॉपी कंस्ट्रक्टर को पसंद करता हूं। आशय स्पष्ट है।


5
.Net में कॉपी कंस्ट्रक्टर नहीं हैं।
पॉप कैटलिन

48
यह सुनिश्चित करता है: नया MyObject (objToCloneFrom) बस एक ctor घोषित करें जो ऑब्जेक्ट को एक पैरामीटर के रूप में क्लोन करने के लिए लेता है।
निक

30
यह एक ही बात नहीं है। आपको इसे हर कक्षा में मैन्युअल रूप से जोड़ना होगा, और आपको यह भी नहीं पता होगा कि क्या आप एक गहरी प्रतिलिपि तैयार कर रहे हैं।
डेव वान डेन आइंड

15
प्रतिलिपि ctor के लिए +1। आपको प्रत्येक प्रकार की वस्तु के लिए मैन्युअल रूप से एक क्लोन () फ़ंक्शन भी लिखना होगा, और इसके साथ शुभकामनाएं जब आपकी कक्षा पदानुक्रम कुछ स्तरों को गहरा हो जाती है।
एंड्रयू ग्रांट

3
हालांकि आप कॉपी कंस्ट्रक्टर के साथ पदानुक्रम खो देते हैं। agiledeveloper.com/articles/cloning072002.htm
होगा

41

सभी सार्वजनिक संपत्तियों की नकल करने के लिए सरल विस्तार विधि। किसी भी वस्तु के लिए काम करता है और वर्ग होने की आवश्यकता नहीं है [Serializable]। अन्य पहुँच स्तर के लिए बढ़ाया जा सकता है।

public static void CopyTo( this object S, object T )
{
    foreach( var pS in S.GetType().GetProperties() )
    {
        foreach( var pT in T.GetType().GetProperties() )
        {
            if( pT.Name != pS.Name ) continue;
            ( pT.GetSetMethod() ).Invoke( T, new object[] 
            { pS.GetGetMethod().Invoke( S, null ) } );
        }
    };
}

15
यह, दुर्भाग्य से, त्रुटिपूर्ण है। यह कॉल करने के बराबर है objectOne.MyProperty = objectTwo.MyProperty (यानी, यह सिर्फ संदर्भ को कॉपी करेगा)। यह गुणों के मूल्यों का क्लोन नहीं करेगा।
एलेक्स नोरक्लिफ

1
एलेक्स नॉरक्लिफ के लिए: सवाल के लेखक ने "प्रत्येक संपत्ति की नकल" के बारे में पूछा, बल्कि फिर क्लोनिंग किया। ज्यादातर मामलों में संपत्तियों के सटीक दोहराव की जरूरत नहीं होती है।
कॉन्स्टेंटिन सलातावोव

1
मैं इस पद्धति का उपयोग करने के बारे में सोचता हूं लेकिन पुनरावृत्ति के साथ। इसलिए यदि किसी संपत्ति का मूल्य एक संदर्भ है, तो एक नई वस्तु बनाएं और फिर से CopyTo को कॉल करें। मैं सिर्फ एक समस्या देखता हूं, कि सभी उपयोग किए गए वर्गों में पैरामीटर के बिना एक कंस्ट्रक्टर होना चाहिए। किसी ने यह पहले से ही कोशिश की? मुझे भी आश्चर्य है कि क्या यह वास्तव में DataRow और DataTable जैसी .net कक्षाओं वाले गुणों के साथ काम करेगा?
कोरियू

33

मैंने अभी CloneExtensionsलाइब्रेरी प्रोजेक्ट बनाया है । यह एक्सप्रेशन ट्री रनटाइम कोड संकलन द्वारा उत्पन्न सरल असाइनमेंट ऑपरेशंस का उपयोग करके तेज, गहरा क्लोन करता है।

इसका इस्तेमाल कैसे करें?

फ़ील्ड्स और प्रॉपर्टीज़ के बीच असाइनमेंट के एक टोन के साथ अपने खुद के Cloneया Copyतरीकों को लिखने के बजाय एक्सप्रेशन ट्री का उपयोग करके प्रोग्राम को अपने लिए करें। GetClone<T>()विस्तार विधि के रूप में चिह्नित विधि आपको अपने उदाहरण पर बस इसे कॉल करने की अनुमति देती है:

var newInstance = source.GetClone();

आप चुन सकते हैं कि एनम sourceका newInstanceउपयोग करने से क्या कॉपी किया जाना चाहिए CloningFlags:

var newInstance 
    = source.GetClone(CloningFlags.Properties | CloningFlags.CollectionItems);

क्या क्लोन किया जा सकता है?

  • आदिम (इंट, यूंट, बाइट, डबल, चार, आदि), ज्ञात अपरिवर्तनीय प्रकार (डेटाइम, टाइमस्पैन, स्ट्रिंग) और प्रतिनिधियों (एक्शन, फ़ंक, आदि सहित)
  • नल
  • टी [] सरणियाँ
  • सामान्य वर्गों और संरचनाओं सहित कस्टम कक्षाएं और संरचनाएं।

निम्नलिखित वर्ग / संरचना सदस्यों को आंतरिक रूप से क्लोन किया जाता है:

  • जनता का मान, पठनीय क्षेत्र नहीं
  • दोनों के साथ सार्वजनिक संपत्तियों के मूल्य प्राप्त होते हैं और एक्सेसर्स सेट करते हैं
  • ICollection को लागू करने वाले प्रकारों के लिए संग्रह आइटम

कितना तेज है?

समाधान तेजी से फिर प्रतिबिंब है, क्योंकि सदस्यों की जानकारी केवल एक बार एकत्र की जानी है, इससे GetClone<T>पहले दिए गए प्रकार के लिए पहली बार उपयोग किया जाता है T

यह क्रमिक-आधारित समाधान से भी तेज है जब आप अधिक क्लोन करते हैं तो एक ही प्रकार के युगल उदाहरण T

और अधिक...

प्रलेखन पर उत्पन्न भावों के बारे में और पढ़ें ।

के लिए नमूना अभिव्यक्ति डीबग सूची List<int>:

.Lambda #Lambda1<System.Func`4[System.Collections.Generic.List`1[System.Int32],CloneExtensions.CloningFlags,System.Collections.Generic.IDictionary`2[System.Type,System.Func`2[System.Object,System.Object]],System.Collections.Generic.List`1[System.Int32]]>(
    System.Collections.Generic.List`1[System.Int32] $source,
    CloneExtensions.CloningFlags $flags,
    System.Collections.Generic.IDictionary`2[System.Type,System.Func`2[System.Object,System.Object]] $initializers) {
    .Block(System.Collections.Generic.List`1[System.Int32] $target) {
        .If ($source == null) {
            .Return #Label1 { null }
        } .Else {
            .Default(System.Void)
        };
        .If (
            .Call $initializers.ContainsKey(.Constant<System.Type>(System.Collections.Generic.List`1[System.Int32]))
        ) {
            $target = (System.Collections.Generic.List`1[System.Int32]).Call ($initializers.Item[.Constant<System.Type>(System.Collections.Generic.List`1[System.Int32])]
            ).Invoke((System.Object)$source)
        } .Else {
            $target = .New System.Collections.Generic.List`1[System.Int32]()
        };
        .If (
            ((System.Byte)$flags & (System.Byte).Constant<CloneExtensions.CloningFlags>(Fields)) == (System.Byte).Constant<CloneExtensions.CloningFlags>(Fields)
        ) {
            .Default(System.Void)
        } .Else {
            .Default(System.Void)
        };
        .If (
            ((System.Byte)$flags & (System.Byte).Constant<CloneExtensions.CloningFlags>(Properties)) == (System.Byte).Constant<CloneExtensions.CloningFlags>(Properties)
        ) {
            .Block() {
                $target.Capacity = .Call CloneExtensions.CloneFactory.GetClone(
                    $source.Capacity,
                    $flags,
                    $initializers)
            }
        } .Else {
            .Default(System.Void)
        };
        .If (
            ((System.Byte)$flags & (System.Byte).Constant<CloneExtensions.CloningFlags>(CollectionItems)) == (System.Byte).Constant<CloneExtensions.CloningFlags>(CollectionItems)
        ) {
            .Block(
                System.Collections.Generic.IEnumerator`1[System.Int32] $var1,
                System.Collections.Generic.ICollection`1[System.Int32] $var2) {
                $var1 = (System.Collections.Generic.IEnumerator`1[System.Int32]).Call $source.GetEnumerator();
                $var2 = (System.Collections.Generic.ICollection`1[System.Int32])$target;
                .Loop  {
                    .If (.Call $var1.MoveNext() != False) {
                        .Call $var2.Add(.Call CloneExtensions.CloneFactory.GetClone(
                                $var1.Current,
                                $flags,


                         $initializers))
                } .Else {
                    .Break #Label2 { }
                }
            }
            .LabelTarget #Label2:
        }
    } .Else {
        .Default(System.Void)
    };
    .Label
        $target
    .LabelTarget #Label1:
}

}

c # कोड को फॉलो करने का एक ही मतलब है:

(source, flags, initializers) =>
{
    if(source == null)
        return null;

    if(initializers.ContainsKey(typeof(List<int>))
        target = (List<int>)initializers[typeof(List<int>)].Invoke((object)source);
    else
        target = new List<int>();

    if((flags & CloningFlags.Properties) == CloningFlags.Properties)
    {
        target.Capacity = target.Capacity.GetClone(flags, initializers);
    }

    if((flags & CloningFlags.CollectionItems) == CloningFlags.CollectionItems)
    {
        var targetCollection = (ICollection<int>)target;
        foreach(var item in (ICollection<int>)source)
        {
            targetCollection.Add(item.Clone(flags, initializers));
        }
    }

    return target;
}

क्या यह बिल्कुल पसंद नहीं है कि आप अपनी Cloneविधि कैसे लिखेंगे List<int>?


2
नुगेट पर इसके मिलने की संभावना क्या है? यह सबसे अच्छा समाधान की तरह लगता है। इसकी तुलना NClone से कैसे की जाती है ?
क्रश करें

मुझे लगता है कि इस उत्तर को और अधिक बार उखाड़ा जाना चाहिए। मैन्युअल रूप से कार्यान्वित ICloneable थकाऊ और त्रुटि-प्रवण है, प्रतिबिंब या क्रमांकन का उपयोग धीमा है यदि प्रदर्शन महत्वपूर्ण है और आपको थोड़े समय के दौरान हजारों वस्तुओं की प्रतिलिपि बनाने की आवश्यकता है।
नाइटकोर

बिल्कुल नहीं, आप प्रतिबिंब के बारे में गलत हैं, आपको बस इसे ठीक से कैश करना चाहिए। नीचे दिए गए मेरे जवाब की जांच करें stackoverflow.com/a/34368738/4711853
रोमा बोरोडोव

31

वैसे मुझे सिल्वरलाइट में ICloneable का उपयोग करने में समस्या हो रही थी, लेकिन मुझे सीरलाइज़ेशन का विचार पसंद आया, मैं XML को सीरलाइज़ कर सकता हूं, इसलिए मैंने ऐसा किया:

static public class SerializeHelper
{
    //Michael White, Holly Springs Consulting, 2009
    //michael@hollyspringsconsulting.com
    public static T DeserializeXML<T>(string xmlData) where T:new()
    {
        if (string.IsNullOrEmpty(xmlData))
            return default(T);

        TextReader tr = new StringReader(xmlData);
        T DocItms = new T();
        XmlSerializer xms = new XmlSerializer(DocItms.GetType());
        DocItms = (T)xms.Deserialize(tr);

        return DocItms == null ? default(T) : DocItms;
    }

    public static string SeralizeObjectToXML<T>(T xmlObject)
    {
        StringBuilder sbTR = new StringBuilder();
        XmlSerializer xmsTR = new XmlSerializer(xmlObject.GetType());
        XmlWriterSettings xwsTR = new XmlWriterSettings();

        XmlWriter xmwTR = XmlWriter.Create(sbTR, xwsTR);
        xmsTR.Serialize(xmwTR,xmlObject);

        return sbTR.ToString();
    }

    public static T CloneObject<T>(T objClone) where T:new()
    {
        string GetString = SerializeHelper.SeralizeObjectToXML<T>(objClone);
        return SerializeHelper.DeserializeXML<T>(GetString);
    }
}

31

यदि आप पहले से ही एक 3 पार्टी एप्लिकेशन का उपयोग कर रहे हैं जैसे ValueInjecter या Automapper , तो आप ऐसा कुछ कर सकते हैं:

MyObject oldObj; // The existing object to clone

MyObject newObj = new MyObject();
newObj.InjectFrom(oldObj); // Using ValueInjecter syntax

इस विधि का उपयोग करने के लिए आपको अपनी वस्तुओं को लागू करने ISerializableया करने की आवश्यकता नहीं है ICloneable। यह एमवीसी / एमवीवीएम पैटर्न के साथ आम है, इसलिए इस तरह के सरल उपकरण बनाए गए हैं।

GitHub पर ValueInjecter गहरी क्लोनिंग नमूना देखें ।


25

सबसे अच्छा एक को लागू करना है विस्तार विधि की तरह

public static T DeepClone<T>(this T originalObject)
{ /* the cloning code */ }

और फिर समाधान द्वारा कहीं भी उपयोग करें

var copy = anyObject.DeepClone();

हमारे पास निम्नलिखित तीन कार्यान्वयन हो सकते हैं:

  1. सीरियलाइज़ेशन द्वारा (सबसे छोटा कोड)
  2. परावर्तन द्वारा - 5x तेज
  3. अभिव्यक्ति पेड़ द्वारा - 20x तेज

सभी लिंक किए गए तरीके अच्छी तरह से काम कर रहे हैं और गहराई से परीक्षण किए गए हैं।


अभिव्यक्ति कोड का उपयोग करके क्लोनिंग कोड जिसे आपने codeproject.com/Articles/1111658/… पर पोस्ट किया है , सुरक्षा अपवाद के साथ .Net फ्रेमवर्क के नए संस्करणों में विफल हो रहा है, ऑपरेशन रनटाइम को अस्थिर कर सकता है , यह मूल रूप से विकृत अभिव्यक्ति के पेड़ के कारण एक अपवाद है, जिसका उपयोग रनटाइम पर फंक उत्पन्न करने के लिए किया जाता है, कृपया जांच लें कि क्या आपके पास कुछ समाधान है। वास्तव में मैंने केवल गहरी पदानुक्रम के साथ जटिल वस्तुओं के साथ मुद्दा देखा है, सरल आसानी से कॉपी हो जाता है
मृणाल काम्बोज

1
ExpressionTree कार्यान्वयन बहुत अच्छा लगता है। यहां तक ​​कि यह परिपत्र संदर्भ और निजी सदस्यों के साथ भी काम करता है। कोई विशेषताओं की जरूरत नहीं है। सबसे अच्छा जवाब मुझे मिला है।
N73k

सबसे अच्छा जवाब, बहुत अच्छा काम किया, आपने मेरा दिन बचा लिया
आदेल मौराड

23

संक्षिप्त उत्तर आपको ICloneable इंटरफ़ेस से विरासत में मिला है और फिर .clone फ़ंक्शन लागू करें। क्लोन को एक सदस्य की प्रतिलिपि बनाना चाहिए और किसी भी सदस्य पर एक गहरी प्रतिलिपि का प्रदर्शन करना चाहिए जिसके लिए इसकी आवश्यकता है, फिर परिणामी वस्तु लौटाएं। यह एक पुनरावर्ती ऑपरेशन है (इसके लिए आवश्यक है कि आप जिस वर्ग के क्लोन बनाना चाहते हैं, उसके सभी सदस्य या तो मूल्य प्रकार हैं या ICloneable को लागू करते हैं और यह कि उनके सदस्य या तो मूल्य प्रकार हैं या ICloneable को लागू करते हैं, और इसी तरह)।

ICloneable का उपयोग करके क्लोनिंग पर अधिक विस्तृत विवरण के लिए, इस लेख को देखें

लंबे जवाब है, "यह निर्भर करता है"। जैसा कि दूसरों ने उल्लेख किया है, ICloneable जेनरिक द्वारा समर्थित नहीं है, को परिपत्र वर्ग के संदर्भों के लिए विशेष विचार की आवश्यकता होती है, और वास्तव में कुछ लोगों द्वारा इसे "गलती" के रूप में देखा जाता है। में .NET फ्रेमवर्क में । क्रमांकन विधि आपकी वस्तुओं पर क्रमिक होने के आधार पर निर्भर करती है, जो कि वे नहीं हो सकती हैं और आपका कोई नियंत्रण नहीं हो सकता है। समुदाय में अभी भी बहुत बहस चल रही है जिस पर "सबसे अच्छा" अभ्यास है। वास्तव में, समाधानों में से कोई भी एक आकार नहीं है जो सभी स्थितियों के लिए सभी सर्वोत्तम अभ्यासों को फिट करता है जैसे कि ICloneable की मूल रूप से व्याख्या की गई थी।

कुछ और विकल्पों (इयान के लिए क्रेडिट) के लिए इस डेवलपर कॉर्नर लेख को देखें ।


1
ICloneable का जेनेरिक इंटरफ़ेस नहीं है, इसलिए उस इंटरफ़ेस का उपयोग करने की अनुशंसा नहीं की जाती है।
कारग

आपका समाधान तब तक काम करता है जब तक कि इसे परिपत्र संदर्भों को संभालने की आवश्यकता नहीं होती है, फिर चीजें जटिल होने लगती हैं, गहरे क्रमांकन का उपयोग करके गहरी क्लोनिंग को लागू करने का प्रयास करना बेहतर होता है।
पॉप कैटलिन

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

19
  1. मूल रूप से आपको ICloneable इंटरफ़ेस को लागू करने और फिर ऑब्जेक्ट संरचना की प्रतिलिपि बनाने का एहसास करने की आवश्यकता है।
  2. यदि यह सभी सदस्यों की गहरी प्रतिलिपि है, तो आपको बीमा (आपके द्वारा चुने गए समाधान पर संबंधित नहीं) की आवश्यकता होती है, जो सभी बच्चों के साथ-साथ क्लोन करने योग्य हो।
  3. कभी-कभी आपको इस प्रक्रिया के दौरान कुछ प्रतिबंधों के बारे में पता होना चाहिए, उदाहरण के लिए यदि आप ORM ऑब्जेक्ट्स को कॉपी करते हैं, तो अधिकांश फ्रेमवर्क सत्र से जुड़ी केवल एक ही वस्तु को अनुमति देते हैं और आप इस ऑब्जेक्ट के क्लोन नहीं बनाते हैं, या यदि संभव हो तो आपको देखभाल करने की आवश्यकता है इन वस्तुओं के सत्र को संलग्न करने के बारे में।

चीयर्स।


4
ICloneable का जेनेरिक इंटरफ़ेस नहीं है, इसलिए उस इंटरफ़ेस का उपयोग करने की अनुशंसा नहीं की जाती है।
कारग

सरल और संक्षिप्त उत्तर सबसे अच्छे हैं।
डेविडगुआटा

17

EDIT: परियोजना बंद कर दी गई है

यदि आप अज्ञात प्रकारों के लिए सही क्लोनिंग चाहते हैं तो आप फास्टक्लोन पर एक नज़र डाल सकते हैं

यह द्विआधारी क्रमांकन की तुलना में लगभग 10 गुना तेजी से काम करने वाली अभिव्यक्ति आधारित क्लोनिंग है और संपूर्ण वस्तु ग्राफ अखंडता को बनाए रखता है।

इसका अर्थ है: यदि आप अपनी पदानुक्रम में एक ही वस्तु के लिए कई बार संदर्भित करते हैं, तो क्लोन का एक ही उदाहरण मधुमक्खी का संदर्भ भी होगा।

क्लोन की जा रही वस्तुओं के लिए इंटरफेस, विशेषताओं या किसी अन्य संशोधन की कोई आवश्यकता नहीं है।


यह एक बहुत उपयोगी लगता है
लकीलीकरी

समग्र प्रणाली की तुलना में एक कोड स्नैपशॉट से काम करना शुरू करना आसान है, विशेष रूप से एक बंद। यह काफी समझ में आता है कि कोई भी पुस्तकालय एक शॉट के साथ सभी समस्याओं को हल नहीं कर सकता है। कुछ आराम करना चाहिए।
19o में TarmoPikaro

1
मैंने आपके समाधान की कोशिश की है और यह अच्छी तरह से काम करता है, धन्यवाद! मुझे लगता है कि इस उत्तर को और अधिक बार उखाड़ा जाना चाहिए। मैन्युअल रूप से कार्यान्वित ICloneable थकाऊ और त्रुटि-प्रवण है, प्रतिबिंब या क्रमांकन का उपयोग धीमा है यदि प्रदर्शन महत्वपूर्ण है और आपको थोड़े समय के दौरान हजारों वस्तुओं की प्रतिलिपि बनाने की आवश्यकता है।
रात का खाना

मैंने इसकी कोशिश की और यह मेरे लिए बिल्कुल भी काम नहीं आया। एक सदस्य असफल अपवाद फेंकता है।
माइकल ब्राउन

यह .NET के नए संस्करणों के साथ काम नहीं करता है और इसे बंद कर दिया जाता है
माइकल सैंडर

14

चीजों को सरल रखें और AutoMapper का उपयोग करें जैसा कि दूसरों ने उल्लेख किया है, एक वस्तु को दूसरे में मैप करने के लिए यह एक साधारण सा पुस्तकालय है ... किसी वस्तु को उसी प्रकार से दूसरे में कॉपी करने के लिए, आपको कोड की तीन लाइनें चाहिए:

MyType source = new MyType();
Mapper.CreateMap<MyType, MyType>();
MyType target = Mapper.Map<MyType, MyType>(source);

लक्ष्य वस्तु अब स्रोत वस्तु की एक प्रति है। पर्याप्त सरल नहीं है? अपने समाधान में हर जगह उपयोग करने के लिए एक विस्तार विधि बनाएं:

public static T Copy<T>(this T source)
{
    T copy = default(T);
    Mapper.CreateMap<T, T>();
    copy = Mapper.Map<T, T>(source);
    return copy;
}

विस्तार विधि का उपयोग निम्नानुसार किया जा सकता है:

MyType copy = source.Copy();

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

1
यह केवल एक उथली प्रति है।
N73k

11

मैं मैन्युअल रूप से गहरी कॉपी सूची <टी> के लिए होने वाली .NET कमी को दूर करने के लिए इसके साथ आया था ।

मैं इसका उपयोग करता हूं:

static public IEnumerable<SpotPlacement> CloneList(List<SpotPlacement> spotPlacements)
{
    foreach (SpotPlacement sp in spotPlacements)
    {
        yield return (SpotPlacement)sp.Clone();
    }
}

और दूसरी जगह पर:

public object Clone()
{
    OrderItem newOrderItem = new OrderItem();
    ...
    newOrderItem._exactPlacements.AddRange(SpotPlacement.CloneList(_exactPlacements));
    ...
    return newOrderItem;
}

मैंने ऐसा करने की कोशिश की, जो ऑनलाइनर के साथ आता है, लेकिन यह संभव नहीं है, उपज के कारण अनाम विधि ब्लॉकों के अंदर काम नहीं कर रहा है।

बेहतर अभी भी, सामान्य सूची का उपयोग करें <T> क्लोनर:

class Utility<T> where T : ICloneable
{
    static public IEnumerable<T> CloneList(List<T> tl)
    {
        foreach (T t in tl)
        {
            yield return (T)t.Clone();
        }
    }
}

10

Q. मैं इस उत्तर को क्यों चुनूंगा?

  • यदि आप सबसे तेज गति चाहते हैं, तो यह उत्तर चुनें। .NET सक्षम है।
  • यदि आप क्लोनिंग का एक बहुत ही आसान तरीका चाहते हैं, तो इस उत्तर को अनदेखा करें।

दूसरे शब्दों में, एक और उत्तर के साथ जाएं जब तक कि आपके पास एक प्रदर्शन अड़चन न हो जिसे फिक्सिंग की आवश्यकता हो, और आप इसे एक प्रोफाइलर के साथ साबित कर सकते हैं

अन्य तरीकों की तुलना में 10 गुना तेज

गहरी क्लोन करने की निम्न विधि है:

  • किसी भी चीज़ की तुलना में तेज़ी से 10x, जिसमें क्रमांकन / डीसेरिएलाइज़ेशन शामिल है;
  • सैद्धांतिक अधिकतम गति .NET के करीब सुंदर रंगमंच सक्षम है।

और विधि ...

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

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

यहां 100,000 क्लोन के लिए सापेक्ष प्रदर्शन अंतर दिखाते हुए कोड का आउटपुट है:

  • नेस्टेड स्ट्रक्चर्स पर नेस्टेड मेम्बरवाइज के लिए 1.08 सेकंड
  • नेस्टेड सदस्य के लिए 4.77 सेकंड नेस्टेड कक्षाओं पर क्लिक करें
  • 39.93 सेकंड के लिए सीरियलाइज़ेशन / डिसेरिएलाइज़ेशन

नेस्टेड मेम्बरवाइज़लोन का उपयोग करके किसी संरचना की नकल करने में लगभग उतना ही तेज है, और एक संरचना की नकल करना सैद्धांतिक अधिकतम गति के करीब सुंदर है। .NET सक्षम है।

Demo 1 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 2 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 3 of deep copy, using class and serialize/deserialize:
  Elapsed time: 00:00:39.9339425,30000000

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

// 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 1 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\n", sw.Elapsed, total);
    }
    {               
        Console.Write("Demo 2 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\n", sw.Elapsed, total);
    }
    {
        Console.Write("Demo 3 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();
}

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

मान प्रकार बनाम संदर्भ प्रकार

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

  • यदि आपके पास " संरचना " है, तो यह एक मूल्य प्रकार है ताकि आप इसे कॉपी कर सकें, और सामग्री को क्लोन किया जाएगा (लेकिन यह केवल एक उथले क्लोन बना देगा जब तक कि आप इस पोस्ट में तकनीकों का उपयोग न करें)।
  • यदि आपके पास " क्लास " है, तो यह एक संदर्भ प्रकार है , इसलिए यदि आप इसे कॉपी करते हैं, तो आप जो कुछ भी कर रहे हैं वह सूचक को कॉपी कर रहा है। एक सच्चे क्लोन बनाने के लिए, आपको अधिक रचनात्मक होना होगा, और मूल्य प्रकार और संदर्भ प्रकारों के बीच अंतर का उपयोग करना होगा जो स्मृति में मूल वस्तु की एक और प्रतिलिपि बनाता है।

मूल्य प्रकार और संदर्भ प्रकारों के बीच अंतर देखें ।

डिबगिंग में सहायता करने के लिए चेकसम

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

कई अन्य थ्रेड्स से कई थ्रेड्स को डिकूप करने के लिए वास्तव में उपयोगी है

इस कोड के लिए एक उत्कृष्ट उपयोग मामला निर्माता / उपभोक्ता पैटर्न को लागू करने के लिए एक नेस्टेड वर्ग या संरचना के एक पंक्ति में क्लोन खिला रहा है।

  • हमारे पास एक (या अधिक) धागे हो सकते हैं जो एक वर्ग को संशोधित करते हैं जो उनके पास होते हैं, फिर इस वर्ग की पूरी प्रतिलिपि को एक में धकेलते हैं ConcurrentQueue
  • हमारे पास तब एक (या अधिक) धागे होते हैं जो इन वर्गों की प्रतियों को बाहर निकालते हैं और उनसे निपटते हैं।

यह अभ्यास में बहुत अच्छी तरह से काम करता है, और हमें एक या एक से अधिक थ्रेड्स (उपभोक्ताओं) से कई थ्रेड्स (उत्पादकों) को हटाने की अनुमति देता है।

और यह विधि बहुत तेजी से अंधाधुंध है: यदि हम नेस्टेड संरचनाओं का उपयोग करते हैं, तो यह नेस्टेड क्लासेसिंग / डिसेरिएलाइजिंग नेस्टेड क्लासेस की तुलना में 35 गुना तेज है, और हमें मशीन पर उपलब्ध सभी थ्रेड्स का लाभ उठाने की अनुमति देता है।

अपडेट करें

जाहिर है, ExpressMapper उपवास के रूप में हाथ कोडिंग की तुलना में तेजी से, अगर नहीं है। मुझे यह देखना होगा कि वे एक प्रोफाइलर के साथ तुलना कैसे करते हैं।


यदि आप एक संरचना की नकल करते हैं, तो आपको एक उथली प्रतिलिपि मिलती है, तो आपको एक गहरी प्रतिलिपि के लिए विशिष्ट कार्यान्वयन की आवश्यकता हो सकती है।
लास वी। कार्लसन जूल 4'15

@ लस्से वी। कार्लसन हां, आप बिलकुल सही हैं, मैंने यह स्पष्ट करने के लिए उत्तर को अपडेट कर दिया है। इस पद्धति का उपयोग संरचनाओं और कक्षाओं की गहरी प्रतियां बनाने के लिए किया जा सकता है । आप इसमें शामिल उदाहरण डेमो कोड चला सकते हैं कि यह कैसे किया जाता है, इसमें एक नेस्टेड संरचना के गहरे क्लोनिंग का उदाहरण है, और एक नेस्टेड क्लास को डीप क्लोनिंग करने का एक और उदाहरण है।
कंटैंगो

9

सामान्य तौर पर, आप ICloneable इंटरफ़ेस को लागू करते हैं और क्लोन को स्वयं कार्यान्वित करते हैं। C # ऑब्जेक्ट में एक अंतर्निहित मेम्बरवाइज़लोन विधि है जो एक उथली प्रतिलिपि का प्रदर्शन करती है जो आपको सभी प्राथमिकताओं के लिए मदद कर सकती है।

एक गहरी प्रतिलिपि के लिए, कोई ऐसा तरीका नहीं है जिससे यह पता चल सके कि स्वचालित रूप से इसे कैसे करना है।


ICloneable का जेनेरिक इंटरफ़ेस नहीं है, इसलिए उस इंटरफ़ेस का उपयोग करने की अनुशंसा नहीं की जाती है।
कारग

8

मैंने इसे प्रतिबिंब के माध्यम से भी लागू किया है। मूल रूप से एक ऐसी विधि थी जो किसी वस्तु के सदस्यों के माध्यम से पुनरावृत्ति करती और उचित रूप से उन्हें नई वस्तु में कॉपी करती। जब यह संदर्भ प्रकारों या संग्रहों तक पहुँच गया, तो मुझे लगता है कि इसने स्वयं पर एक पुनरावर्ती कॉल किया। प्रतिबिंब महंगा है, लेकिन यह बहुत अच्छी तरह से काम किया।


8

यहाँ एक गहरी कॉपी कार्यान्वयन है:

public static object CloneObject(object opSource)
{
    //grab the type and create a new instance of that type
    Type opSourceType = opSource.GetType();
    object opTarget = CreateInstanceOfType(opSourceType);

    //grab the properties
    PropertyInfo[] opPropertyInfo = opSourceType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

    //iterate over the properties and if it has a 'set' method assign it from the source TO the target
    foreach (PropertyInfo item in opPropertyInfo)
    {
        if (item.CanWrite)
        {
            //value types can simply be 'set'
            if (item.PropertyType.IsValueType || item.PropertyType.IsEnum || item.PropertyType.Equals(typeof(System.String)))
            {
                item.SetValue(opTarget, item.GetValue(opSource, null), null);
            }
            //object/complex types need to recursively call this method until the end of the tree is reached
            else
            {
                object opPropertyValue = item.GetValue(opSource, null);
                if (opPropertyValue == null)
                {
                    item.SetValue(opTarget, null, null);
                }
                else
                {
                    item.SetValue(opTarget, CloneObject(opPropertyValue), null);
                }
            }
        }
    }
    //return the new item
    return opTarget;
}

2
यह सदस्यवार क्लोन की तरह दिखता है क्योंकि संदर्भ प्रकार के गुणों के बारे में जानकारी नहीं है
sll

1
यदि आप अंधाधुंध तेज़ प्रदर्शन चाहते हैं, तो इस कार्यान्वयन के लिए न जाएं: यह प्रतिबिंब का उपयोग करता है, इसलिए यह इतना तेज़ नहीं होगा। इसके विपरीत, "समय से पहले ऑप्टिमाइज़ेशन सभी बुराई का है", इसलिए जब तक आप एक प्रोफाइलर को नहीं चलाते हैं, तब तक प्रदर्शन पक्ष की उपेक्षा करें।
कंटैंगो

1
CreateInstanceOfType परिभाषित नहीं है?
मॉन्स्टरमोरपीजी

यह अंतराल पर विफल रहता है: "गैर-स्थैतिक विधि को एक लक्ष्य की आवश्यकता होती है।"
मि।

8

जैसा कि मुझे एक क्लोनर नहीं मिला है जो विभिन्न परियोजनाओं में मेरी सभी आवश्यकताओं को पूरा करता है, मैंने एक गहरा क्लोनर बनाया है जिसे क्लोनरों की आवश्यकताओं को पूरा करने के लिए मेरे कोड को अपनाने के बजाय अलग-अलग कोड संरचनाओं में कॉन्फ़िगर और अनुकूलित किया जा सकता है। इसके द्वारा कोड में एनोटेशन जोड़कर हासिल किया गया है जिसे क्लोन किया जाएगा या आप कोड को छोड़ दें क्योंकि यह डिफ़ॉल्ट व्यवहार है। यह प्रतिबिंब का उपयोग करता है, कैश टाइप करता है और तेज गति पर आधारित होता है । क्लोनिंग प्रक्रिया बहुत बड़ी मात्रा में डेटा और एक उच्च वस्तु पदानुक्रम (अन्य प्रतिबिंब / क्रमांकन आधारित एल्गोरिदम की तुलना में) के लिए बहुत तेज़ है।

https://github.com/kalisohn/CloneBehave

नगेट पैकेज के रूप में भी उपलब्ध: https://www.nuget.org/packages/Clone.Behave/1.0.0

उदाहरण के लिए: निम्न कोड पता को गहरा करेगा, लेकिन केवल _currentJob फ़ील्ड की एक उथली प्रति निष्पादित करें।

public class Person 
{
  [DeepClone(DeepCloneBehavior.Shallow)]
  private Job _currentJob;      

  public string Name { get; set; }

  public Job CurrentJob 
  { 
    get{ return _currentJob; }
    set{ _currentJob = value; }
  }

  public Person Manager { get; set; }
}

public class Address 
{      
  public Person PersonLivingHere { get; set; }
}

Address adr = new Address();
adr.PersonLivingHere = new Person("John");
adr.PersonLivingHere.BestFriend = new Person("James");
adr.PersonLivingHere.CurrentJob = new Job("Programmer");

Address adrClone = adr.Clone();

//RESULT
adr.PersonLivingHere == adrClone.PersonLivingHere //false
adr.PersonLivingHere.Manager == adrClone.PersonLivingHere.Manager //false
adr.PersonLivingHere.CurrentJob == adrClone.PersonLivingHere.CurrentJob //true
adr.PersonLivingHere.CurrentJob.AnyProperty == adrClone.PersonLivingHere.CurrentJob.AnyProperty //true

7

कोड जनरेटर

हमने मैन्युअल क्रियान्वयन से लेकर प्रतिबिंब तक क्रमबद्धता से बहुत सारे विचारों को देखा है और मैं CGbR कोड जनरेटर का उपयोग करके पूरी तरह से अलग दृष्टिकोण का प्रस्ताव करना चाहता हूं । उत्पन्न क्लोन विधि मेमोरी और सीपीयू कुशल है और मानक डेटाकंट्रेक्टरलाइज़र के रूप में 300 गुना तेज है।

आपको केवल एक आंशिक वर्ग परिभाषा की जरूरत है ICloneableऔर जनरेटर बाकी काम करता है:

public partial class Root : ICloneable
{
    public Root(int number)
    {
        _number = number;
    }
    private int _number;

    public Partial[] Partials { get; set; }

    public IList<ulong> Numbers { get; set; }

    public object Clone()
    {
        return Clone(true);
    }

    private Root()
    {
    }
} 

public partial class Root
{
    public Root Clone(bool deep)
    {
        var copy = new Root();
        // All value types can be simply copied
        copy._number = _number; 
        if (deep)
        {
            // In a deep clone the references are cloned 
            var tempPartials = new Partial[Partials.Length];
            for (var i = 0; i < Partials.Length; i++)
            {
                var value = Partials[i];
                value = value.Clone(true);
                tempPartials[i] = value;
            }
            copy.Partials = tempPartials;
            var tempNumbers = new List<ulong>(Numbers.Count);
            for (var i = 0; i < Numbers.Count; i++)
            {
                var value = Numbers[i];
                tempNumbers.Add(value);
            }
            copy.Numbers = tempNumbers;
        }
        else
        {
            // In a shallow clone only references are copied
            copy.Partials = Partials; 
            copy.Numbers = Numbers; 
        }
        return copy;
    }
}

नोट: नवीनतम संस्करण में अधिक अशक्त चेक हैं, लेकिन मैंने उन्हें बेहतर समझ के लिए छोड़ दिया।


6

मुझे इस तरह के कॉपीकॉन्स्ट्रक्टर्स पसंद हैं:

    public AnyObject(AnyObject anyObject)
    {
        foreach (var property in typeof(AnyObject).GetProperties())
        {
            property.SetValue(this, property.GetValue(anyObject));
        }
        foreach (var field in typeof(AnyObject).GetFields())
        {
            field.SetValue(this, field.GetValue(anyObject));
        }
    }

यदि आपके पास उन्हें जोड़ने के लिए और अधिक चीजें हैं


6

इस विधि ने मेरे लिए समस्या हल कर दी:

private static MyObj DeepCopy(MyObj source)
        {

            var DeserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };

            return JsonConvert.DeserializeObject<MyObj >(JsonConvert.SerializeObject(source), DeserializeSettings);

        }

इसे इस तरह उपयोग करें: MyObj a = DeepCopy(b);


6

यहां एक समाधान तेजी से और आसान है जो मेरे लिए सीरियललाइज़ेशन / डिसेरिएलाइज़ेशन पर रिले किए बिना काम करता है।

public class MyClass
{
    public virtual MyClass DeepClone()
    {
        var returnObj = (MyClass)MemberwiseClone();
        var type = returnObj.GetType();
        var fieldInfoArray = type.GetRuntimeFields().ToArray();

        foreach (var fieldInfo in fieldInfoArray)
        {
            object sourceFieldValue = fieldInfo.GetValue(this);
            if (!(sourceFieldValue is MyClass))
            {
                continue;
            }

            var sourceObj = (MyClass)sourceFieldValue;
            var clonedObj = sourceObj.DeepClone();
            fieldInfo.SetValue(returnObj, clonedObj);
        }
        return returnObj;
    }
}

संपादित करें : की आवश्यकता है

    using System.Linq;
    using System.Reflection;

यह है कि मैं इसे कैसे इस्तेमाल किया

public MyClass Clone(MyClass theObjectIneededToClone)
{
    MyClass clonedObj = theObjectIneededToClone.DeepClone();
}

5

इन कदमों का अनुसरण करें:

  • ISelf<T>केवल पढ़ने वाली Selfसंपत्ति के साथ परिभाषित करें , जो वापस आती है T, और ICloneable<out T>, जो ISelf<T>एक विधि से निकलती है और इसमें शामिल है T Clone()
  • फिर एक CloneBaseप्रकार को परिभाषित करें जो एक protected virtual generic VirtualCloneकास्टिंग MemberwiseCloneको पारित-प्रकार में लागू करता है ।
  • प्रत्येक व्युत्पन्न प्रकार को VirtualCloneबेस क्लोन विधि को लागू करके लागू किया जाना चाहिए और फिर जो कुछ भी करने की आवश्यकता होती है, वह उन प्रकार के उन पहलुओं को ठीक से करने के लिए किया जाता है, जिन्हें मूल VirtualClone विधि ने अभी तक संभाला नहीं है।

अधिकतम विरासत की बहुमुखी प्रतिभा के लिए, सार्वजनिक क्लोनिंग कार्यक्षमता को उजागर करने वाली कक्षाएं होनी चाहिए sealed, लेकिन एक आधार वर्ग से प्राप्त होती हैं जो क्लोनिंग की कमी को छोड़कर अन्यथा समान है। स्पष्ट क्लोन योग्य प्रकार के चर को पार करने के बजाय, प्रकार का एक पैरामीटर लें ICloneable<theNonCloneableType>। यह एक ऐसी दिनचर्या की अनुमति देगा जो एक क्लोन व्युत्पन्न के Fooसाथ काम करने के लिए एक क्लोन करने योग्य व्युत्पन्न की अपेक्षा करता है DerivedFoo, लेकिन गैर-क्लोन करने योग्य डेरिवेटिव के निर्माण की अनुमति भी देता है Foo



4

मैंने स्वीकृत उत्तर का एक संस्करण बनाया है जो '[सीरियल] और' [डाटा कांट्रेक्ट] दोनों के साथ काम करता है। इसे लिखे हुए कुछ समय हो गया है, लेकिन अगर मुझे सही से याद है तो [DataContract] को एक अलग सीरियल-राइटर की जरूरत थी।

आवश्यक प्रणाली, System.IO, System.Runtime.Serialization, System.Runtime.Serialization.Formatters.Binary, System.Xml ;

public static class ObjectCopier
{

    /// <summary>
    /// Perform a deep Copy of an object that is marked with '[Serializable]' or '[DataContract]'
    /// </summary>
    /// <typeparam name="T">The type of object being copied.</typeparam>
    /// <param name="source">The object instance to copy.</param>
    /// <returns>The copied object.</returns>
    public static T Clone<T>(T source)
    {
        if (typeof(T).IsSerializable == true)
        {
            return CloneUsingSerializable<T>(source);
        }

        if (IsDataContract(typeof(T)) == true)
        {
            return CloneUsingDataContracts<T>(source);
        }

        throw new ArgumentException("The type must be Serializable or use DataContracts.", "source");
    }


    /// <summary>
    /// Perform a deep Copy of an object that is marked with '[Serializable]'
    /// </summary>
    /// <remarks>
    /// Found on http://stackoverflow.com/questions/78536/cloning-objects-in-c-sharp
    /// Uses code found on CodeProject, which allows free use in third party apps
    /// - http://www.codeproject.com/KB/tips/SerializedObjectCloner.aspx
    /// </remarks>
    /// <typeparam name="T">The type of object being copied.</typeparam>
    /// <param name="source">The object instance to copy.</param>
    /// <returns>The copied object.</returns>
    public static T CloneUsingSerializable<T>(T source)
    {
        if (!typeof(T).IsSerializable)
        {
            throw new ArgumentException("The type must be serializable.", "source");
        }

        // Don't serialize a null object, simply return the default for that object
        if (Object.ReferenceEquals(source, null))
        {
            return default(T);
        }

        IFormatter formatter = new BinaryFormatter();
        Stream stream = new MemoryStream();
        using (stream)
        {
            formatter.Serialize(stream, source);
            stream.Seek(0, SeekOrigin.Begin);
            return (T)formatter.Deserialize(stream);
        }
    }


    /// <summary>
    /// Perform a deep Copy of an object that is marked with '[DataContract]'
    /// </summary>
    /// <typeparam name="T">The type of object being copied.</typeparam>
    /// <param name="source">The object instance to copy.</param>
    /// <returns>The copied object.</returns>
    public static T CloneUsingDataContracts<T>(T source)
    {
        if (IsDataContract(typeof(T)) == false)
        {
            throw new ArgumentException("The type must be a data contract.", "source");
        }

        // ** Don't serialize a null object, simply return the default for that object
        if (Object.ReferenceEquals(source, null))
        {
            return default(T);
        }

        DataContractSerializer dcs = new DataContractSerializer(typeof(T));
        using(Stream stream = new MemoryStream())
        {
            using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(stream))
            {
                dcs.WriteObject(writer, source);
                writer.Flush();
                stream.Seek(0, SeekOrigin.Begin);
                using (XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(stream, XmlDictionaryReaderQuotas.Max))
                {
                    return (T)dcs.ReadObject(reader);
                }
            }
        }
    }


    /// <summary>
    /// Helper function to check if a class is a [DataContract]
    /// </summary>
    /// <param name="type">The type of the object to check.</param>
    /// <returns>Boolean flag indicating if the class is a DataContract (true) or not (false) </returns>
    public static bool IsDataContract(Type type)
    {
        object[] attributes = type.GetCustomAttributes(typeof(DataContractAttribute), false);
        return attributes.Length == 1;
    }

} 

4

ठीक है, इस पोस्ट में प्रतिबिंब के साथ कुछ स्पष्ट उदाहरण हैं, लेकिन बीयूटी प्रतिबिंब आमतौर पर धीमा होता है, जब तक कि आप इसे ठीक से कैश नहीं करना शुरू करते हैं।

यदि आप इसे ठीक से कैश करते हैं, तो यह 4,6 से 1000000 ऑब्जेक्ट को गहरा करेगा (वॉचर द्वारा मापा गया)।

static readonly Dictionary<Type, PropertyInfo[]> ProperyList = new Dictionary<Type, PropertyInfo[]>();

जैसे आप कैश्ड गुण लेते हैं या शब्दकोश में नया जोड़ते हैं और उन्हें बस उपयोग करते हैं

foreach (var prop in propList)
{
        var value = prop.GetValue(source, null);   
        prop.SetValue(copyInstance, value, null);
}

एक अन्य उत्तर में मेरी पोस्ट में पूर्ण कोड की जाँच

https://stackoverflow.com/a/34365709/4711853


2
कॉलिंग prop.GetValue(...)अभी भी प्रतिबिंब है और कैश नहीं किया जा सकता है। एक अभिव्यक्ति के पेड़ में इसके संकलित हालांकि, इतनी तेजी से
Tseng

4

जैसा कि इस प्रश्न के लगभग सभी उत्तर असंतोषजनक रहे हैं या स्पष्ट रूप से मेरी स्थिति में काम नहीं करते हैं, मैंने AnyClone को अधिकृत किया है जो पूरी तरह से प्रतिबिंब के साथ लागू किया गया है और यहां सभी आवश्यकताओं को हल किया गया है। मैं जटिल संरचना के साथ एक जटिल परिदृश्य में काम करने के लिए क्रमांकन प्राप्त करने में असमर्थ था, और IClonableआदर्श से कम है - वास्तव में यह आवश्यक भी नहीं होना चाहिए।

स्टैंडर्ड उपेक्षा विशेषताओं का उपयोग का समर्थन कर रहे [IgnoreDataMember], [NonSerialized]। जटिल संग्रह का समर्थन करता है, बसने वालों के बिना गुण, आसानी से खेतों आदि।

मुझे आशा है कि यह किसी अन्य व्यक्ति की मदद करता है जो उसी समस्याओं में भाग गया था जो मैंने किया था।


4

अस्वीकरण: मैं उल्लिखित पैकेज का लेखक हूं।

मैं आश्चर्यचकित था कि 2019 में इस प्रश्न के शीर्ष उत्तर अभी भी क्रमांकन या प्रतिबिंब का उपयोग कैसे करते हैं।

सीरियलाइज़ेशन सीमित है (विशेषताओं, विशिष्ट बिल्डरों, आदि की आवश्यकता है) और बहुत धीमा है

BinaryFormatterSerializableविशेषता की आवश्यकता होती है , JsonConverterएक पैरामीटर रहित निर्माता या विशेषताओं की आवश्यकता होती है, न तो केवल फ़ील्ड या इंटरफेस को बहुत अच्छी तरह से पढ़ा जाता है और दोनों आवश्यक के लिए 10-30x धीमे होते हैं।

अभिव्यक्ति पेड़

आप इसके बजाय अभिव्यक्ति पेड़ या परावर्तन का उपयोग कर सकते हैं केवल एक बार क्लोनिंग कोड उत्पन्न करने के लिए मान लें, फिर धीमे प्रतिबिंब या क्रमांकन के बजाय उस संकलित कोड का उपयोग करें।

स्वयं समस्या के आने के बाद और कोई संतोषजनक समाधान न मिलने पर, मैंने एक ऐसा पैकेज बनाने का निर्णय लिया, जो हर प्रकार के साथ काम करता हो और लगभग उतना ही तेज़ हो जितना कि कस्टम लिखित कोड

आप GitHub: https://github.com/marcelltoth/ObjectCloner पर प्रोजेक्ट पा सकते हैं

प्रयोग

आप इसे NuGet से इंस्टॉल कर सकते हैं। या तो ObjectClonerपैकेज प्राप्त करें और इसका उपयोग करें:

var clone = ObjectCloner.DeepClone(original);

या यदि आप एक्सटेंशन के साथ अपने ऑब्जेक्ट प्रकार को प्रदूषित करने से बुरा नहीं मानते हैं, तो ObjectCloner.Extensionsलिखें:

var clone = original.DeepClone();

प्रदर्शन

एक वर्ग पदानुक्रम की क्लोनिंग का एक सरल बेंचमार्क दर्शाया गया था ~ परावर्तन का उपयोग करने की तुलना में तेजी से 3x, ~ न्यूटनसॉफ्ट की तुलना में तेजी से 12x.Json क्रमांकन और अत्यधिक सुझाए गए की तुलना में ~ 36x तेज BinaryFormatter

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