c# - كائنات الاستنساخ العميق




.net clone (20)

أريد أن أفعل شيئًا مثل:

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

ثم قم بإجراء تغييرات على الكائن الجديد الذي لا ينعكس في الكائن الأصلي.

لا أحتاج في كثير من الأحيان إلى هذه الوظيفة ، لذا عندما كان ذلك ضروريًا ، لجأت إلى إنشاء كائن جديد ثم نسخ كل خاصية على حدة ، ولكن ذلك يتركني دائمًا شعورًا بأن هناك طريقة أفضل أو أكثر أناقة في التعامل الوضع.

كيف يمكنني نسخ أو نسخ كائن ما بحيث يمكن تعديل الكائن المستنسخ دون حدوث أي تغييرات في الكائن الأصلي؟


Code Generator

We have seen a lot of ideas from serialization over manual implementation to reflection and I want to propose a totally different approach using the CGbR Code Generator . The generate clone method is memory and CPU efficient and therefor 300x faster as the standard DataContractSerializer.

All you need is a partial class definition with ICloneable and the generator does the rest:

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

Note: Latest version has a more null checks, but I left them out for better understanding.


Q. Why would I choose this answer?

  • Choose this answer if you want the fastest speed .NET is capable of.
  • Ignore this answer if you want a really, really easy method of cloning.

In other words, go with another answer unless you have a performance bottleneck that needs fixing, and you can prove it with a profiler .

10x faster than other methods

The following method of performing a deep clone is:

  • 10x faster than anything that involves serialization/deserialization;
  • Pretty darn close to the theoretical maximum speed .NET is capable of.

And the method ...

For ultimate speed, you can use Nested MemberwiseClone to do a deep copy . Its almost the same speed as copying a value struct, and is much faster than (a) reflection or (b) serialization (as described in other answers on this page).

Note that if you use Nested MemberwiseClone for a deep copy , you have to manually implement a ShallowCopy for each nested level in the class, and a DeepCopy which calls all said ShallowCopy methods to create a complete clone. This is simple: only a few lines in total, see the demo code below.

Here is the output of the code showing the relative performance difference for 100,000 clones:

  • 1.08 seconds for Nested MemberwiseClone on nested structs
  • 4.77 seconds for Nested MemberwiseClone on nested classes
  • 39.93 seconds for Serialization/Deserialization

Using Nested MemberwiseClone on a class almost as fast as copying a struct, and copying a struct is pretty darn close to the theoretical maximum speed .NET is capable of.

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

To understand how to do a deep copy using MemberwiseCopy, here is the demo project that was used to generate the times above:

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

Then, call the demo from main:

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

Again, note that if you use Nested MemberwiseClone for a deep copy , you have to manually implement a ShallowCopy for each nested level in the class, and a DeepCopy which calls all said ShallowCopy methods to create a complete clone. This is simple: only a few lines in total, see the demo code above.

Value types vs. References Types

Note that when it comes to cloning an object, there is is a big difference between a " struct " and a " class ":

  • If you have a " struct ", it's a value type so you can just copy it, and the contents will be cloned (but it will only make a shallow clone unless you use the techniques in this post).
  • If you have a " class ", it's a reference type , so if you copy it, all you are doing is copying the pointer to it. To create a true clone, you have to be more creative, and use differences between value types and references types which creates another copy of the original object in memory.

See differences between value types and references types .

Checksums to aid in debugging

  • Cloning objects incorrectly can lead to very difficult-to-pin-down bugs. In production code, I tend to implement a checksum to double check that the object has been cloned properly, and hasn't been corrupted by another reference to it. This checksum can be switched off in Release mode.
  • I find this method quite useful: often, you only want to clone parts of the object, not the entire thing.

Really useful for decoupling many threads from many other threads

One excellent use case for this code is feeding clones of a nested class or struct into a queue, to implement the producer / consumer pattern.

  • We can have one (or more) threads modifying a class that they own, then pushing a complete copy of this class into a ConcurrentQueue .
  • We then have one (or more) threads pulling copies of these classes out and dealing with them.

This works extremely well in practice, and allows us to decouple many threads (the producers) from one or more threads (the consumers).

And this method is blindingly fast too: if we use nested structs, it's 35x faster than serializing/deserializing nested classes, and allows us to take advantage of all of the threads available on the machine.

تحديث

Apparently, ExpressMapper is as fast, if not faster, than hand coding such as above. I might have to see how they compare with a profiler.


أنا أفضل منشئ نسخة إلى استنساخ. القصد أكثر وضوحا.


إذا كنت تريد الاستنساخ الحقيقي لأنواع غير معروفة ، يمكنك إلقاء نظرة على fastclone .

هذا الاستنساخ المبني على التعبير يعمل حوالي 10 مرات أسرع من التسلسل الثنائي والحفاظ على تكامل رسومات الكائنات الكاملة.

هذا يعني: إذا قمت بالإشارة عدة مرات إلى نفس الكائن في hierachy ، فإن الاستنساخ سيكون له مثيل واحد يتم الرجوع إليه.

ليست هناك حاجة للواجهات أو السمات أو أي تعديل آخر للكائنات التي يتم استنساخها.


اتبع هذه الخطوات:

  • Define an ISelf<T> with a read-only Self property that returns T , and ICloneable<out T> , which derives from ISelf<T> and includes a method T Clone() .
  • Then define a CloneBase type which implements a protected virtual generic VirtualClone casting MemberwiseClone to the passed-in type.
  • Each derived type should implement VirtualClone by calling the base clone method and then doing whatever needs to be done to properly clone those aspects of the derived type which the parent VirtualClone method hasn't yet handled.

For maximum inheritance versatility, classes exposing public cloning functionality should be sealed , but derive from a base class which is otherwise identical except for the lack of cloning. Rather than passing variables of the explicit clonable type, take a parameter of type ICloneable<theNonCloneableType> . This will allow a routine that expects a cloneable derivative of Foo to work with a cloneable derivative of DerivedFoo , but also allow the creation of non-cloneable derivatives of Foo .


الأفضل هو تنفيذ طريقة تمديد مثل

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

ثم استخدامها في أي مكان في الحل من قبل

var copy = anyObject.DeepClone();

يمكننا الحصول على التطبيقات الثلاثة التالية:

  1. عن طريق التسلسل (أقصر رمز)
  2. بواسطة انعكاس - 5x أسرع
  3. بواسطة أشجار التعبير - 20x أسرع

جميع الطرق المرتبطة تعمل جيدًا وتم اختبارها بعمق.


بعد الكثير من القراءة حول العديد من الخيارات المرتبطة هنا ، والحلول الممكنة لهذه المشكلة ، أعتقد أن جميع الخيارات ملخصة بشكل جيد في ارتباط Ian P (جميع الخيارات الأخرى هي اختلافات تلك) ويتم توفير أفضل الحلول من قبل agiledeveloper.com/articles/cloning072002.htm على التعليقات السؤال.

لذا سأقوم بنسخ الأجزاء ذات الصلة من هذين المراجعين هنا. بهذه الطريقة يمكننا الحصول على:

أفضل شيء للقيام به لاستنساخ الكائنات في ج حادة!

أولاً وقبل كل شيء ، هذه كلها خياراتنا:

المادة Fast Deep Copy by Express Trees لها أيضًا مقارنة أداء الاستنساخ بواسطة Serialization و Reflection و Expression Trees.

لماذا اخترت ICloneable (أي يدوياً)

agiledeveloper.com/articles/cloning072002.htm .

يدور كل مقاله حول مثال يحاول أن يكون قابلاً للتطبيق في معظم الحالات ، باستخدام ثلاثة أشياء: الشخص ، العقل والمدينة . نريد استنساخ الشخص الذي سيكون له دماغه الخاص ولكن نفس المدينة. يمكنك إما تصوير جميع المشاكل أي من الطرق الأخرى المذكورة أعلاه يمكن أن تجلب أو تقرأ المقالة.

هذه هي النسخة المعدلة قليلاً من استنتاجه:

يؤدي نسخ كائن بتحديد New متبوعًا باسم الفئة غالبًا إلى رمز غير قابل للتوسعة. استخدام الاستنساخ ، وتطبيق نمط النموذج الأولي ، هو أفضل طريقة لتحقيق ذلك. ومع ذلك ، يمكن استخدام استنساخ كما هو مذكور في C # (وجافا) أن يكون إشكالية تماما كذلك. من الأفضل توفير منشئ نسخة محمي (غير عام) واستدعاء ذلك من أسلوب النسخ. هذا يعطينا القدرة على تفويض مهمة إنشاء كائن إلى مثيل للفئة نفسها ، وبالتالي توفير القابلية للتوسعة وأيضاً إنشاء الكائنات بأمان باستخدام مُنشئ النسخ المحمي.

نأمل أن يؤدي هذا التنفيذ إلى توضيح الأمور:

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 [email protected]
This is person with [email protected]
SkilledPerson: This is person with [email protected]
SkilledPerson: This is person with [email protected]

لاحظ أنه إذا احتفظنا بعدد من الكائنات ، فإن النسخة المطابقة هنا ستحتفظ بالعدد الصحيح لعدد الكائنات.


حسنًا ، كنت أواجه مشكلات في استخدام ICloneable في Silverlight ، ولكن أعجبتني فكرة التحويل ، يمكنني إنشاء XML ، لذا فعلت هذا:

static public class SerializeHelper
{
    //Michael White, Holly Springs Consulting, 2009
    //[email protected]
    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);
    }
}

طريقة تمديد بسيطة لنسخ جميع الخصائص العامة. يعمل لأي كائنات ولا يتطلب أن يكون الفصل [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 ) } );
        }
    };
}

في حين أن الممارسة المعتادة هي تنفيذ واجهة ICloneable (الموصوفة here ، لذلك لن أجتر) ، وهنا آلة نسخ كائن مستنسخ عميق وجدت في مشروع Code منذ فترة 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.", "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 (January 10 2015) أعتقد أنني سأعود إلى هذا ، لذكر أنني بدأت مؤخراً في استخدام (Newtonsoft) Json للقيام بذلك ، يجب أن تكون أخف وزناً ، وتجنب الزيادة في علامات [Serializable]. (أشار NBatconway في التعليقات إلى أنه لا يتم استنساخ الأعضاء من القطاع الخاص باستخدام طريقة 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);
}

لقد توصلت إلى هذا للتغلب على عيب .NET بعد أن يدويا نسخ قائمة عميقة <T>.

انا استعمل هذا:

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

حاولت أن أضع أفايلر على هذا الأمر ، لكن هذا غير ممكن ، لأن الغلة لا تعمل داخل كتل طريقة مجهولة.

Better still, use generic List<T> cloner:

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

لقد قمت للتو بإنشاء مشروع مكتبة CloneExtensions . وهو يقوم بإجراء نسخ سريع وعميق باستخدام عمليات تخصيص بسيطة ناتجة عن تجميع رموز وقت تشغيل Tree Expression Tree.

كيفية استخدامها؟

بدلاً من كتابة أساليب Copy أو Copy الخاصة بك بنغمة الواجبات بين الحقول والخصائص ، اجعل البرنامج يقوم بذلك بنفسك باستخدام Expression Tree. يتيح لك أسلوب GetClone<T>() وضع علامة عليه كأسلوب ملحق إمكانية الاتصال به على سبيل المثال الخاص بك:

var newInstance = source.GetClone();

يمكنك اختيار ما يجب نسخه من source إلى newInstance باستخدام التعداد CloningFlags :

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

ما الذي يمكن استنساخه؟

  • بدائي (int، uint، byte، double، char، etc.) ، أنواع غير معروفة (DateTime ، TimeSpan ، String) والمندوبون (بما في ذلك Action ، Func ، إلخ)
  • قيم الفارغة
  • T [] المصفوفات
  • الفئات والهيكل المخصص ، بما في ذلك الفئات العامة والبنى.

بعد استنساخ أعضاء الفئة / البنية داخليًا:

  • قيم العامة ، وليس الحقول للقراءة فقط
  • قيم الممتلكات العامة مع الحصول على وتعيين accessors
  • جمع البنود لأنواع تنفيذ ICollection

كم هو سريع؟

الحل أسرع من الانعكاس ، لأنه يجب تجميع معلومات الأعضاء مرة واحدة فقط ، قبل استخدام GetClone<T> لأول مرة للنوع المعطى T

كما أنه أسرع من الحل القائم على التسلسل عند استنساخ أكثر من مثيلات زوجين من نفس النوع T

و اكثر...

قراءة المزيد حول التعبيرات التي تم إنشاؤها على documentation .

إدراج تعبير تعبير نموذج لقائمة 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 # code:

(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> ؟


Here a solution fast and easy that worked for me without relaying on Serialization/Deserialization.

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

EDIT : requires

    using System.Linq;
    using System.Reflection;

That's How I used it

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

Here is a deep copy implementation:

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

I like Copyconstructors like that:

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

If you have more things to copy add them


I think you can try this.

MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = new MyObject(myObj); //DeepClone it

If your Object Tree is Serializeable you could also use something like this

static public MyClass Clone(MyClass myClass)
{
    MyClass clone;
    XmlSerializer ser = new XmlSerializer(typeof(MyClass), _xmlAttributeOverrides);
    using (var ms = new MemoryStream())
    {
        ser.Serialize(ms, myClass);
        ms.Position = 0;
        clone = (MyClass)ser.Deserialize(ms);
    }
    return clone;
}

be informed that this Solution is pretty easy but it's not as performant as other solutions may be.

And be sure that if the Class grows, there will still be only those fields cloned, which also get serialized.


In general, you implement the ICloneable interface and implement Clone yourself. C# objects have a built-in MemberwiseClone method that performs a shallow copy that can help you out for all the primitives.

For a deep copy, there is no way it can know how to automatically do it.


Ok, there are some obvious example with reflection in this post, BUT reflection is usually slow, until you start to cache it properly.

if you'll cache it properly, than it'll deep clone 1000000 object by 4,6s (measured by Watcher).

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

than you take cached properties or add new to dictionary and use them simply

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

full code check in my post in another answer

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


This method solved the problem for me:

private static MyObj DeepCopy(MyObj source)
        {

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

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

        }

Use it like this: MyObj a = DeepCopy(b);





clone