Глубокое клонирование объектов

Я хочу сделать что-то вроде:

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

А затем внесите изменения в новый объект, которые не отражены в исходном объекте.

Мне не часто нужны эти функции, поэтому, когда это было необходимо, я прибегал к созданию нового объекта, а затем копировал каждое свойство по отдельности, но это всегда оставляло у меня ощущение, что есть лучший или более элегантный способ справиться с ситуацией.

Как я могу клонировать или выполнить глубокое копирование объекта, чтобы клонированный объект мог быть изменен без каких-либо изменений, отраженных в исходном объекте?

вопрос задан 17.09.2008
NakedBrunch
29554 репутация

39 ответов


  • 0 рейтинг

    При использовании Marc Gravells protobuf-net в качестве сериализатора принятый ответ нуждается в незначительных изменениях, поскольку копируемый объект не будет приписан [Serializable] и, следовательно, не сериализуется, а метод Clone вызовет исключение.
    Я изменил его для работы с protobuf-net:

    public static T Clone(this T source)
    {
        if(Attribute.GetCustomAttribute(typeof(T), typeof(ProtoBuf.ProtoContractAttribute))
               == null)
        {
            throw new ArgumentException("Type has no ProtoContract!", "source");
        }
    
        if(Object.ReferenceEquals(source, null))
        {
            return default(T);
        }
    
        IFormatter formatter = ProtoBuf.Serializer.CreateFormatter();
        using (Stream stream = new MemoryStream())
        {
            formatter.Serialize(stream, source);
            stream.Seek(0, SeekOrigin.Begin);
            return (T)formatter.Deserialize(stream);
        }
    }
    

    Это проверяет наличие атрибута [ProtoContract] и использует собственный форматер protobufs для сериализации объекта.

    ответ дан Basti M, с репутацией 5016, 22.08.2015
  • 0 рейтинг

    Причиной не использовать ICloneable является , а не , поскольку он не имеет универсального интерфейса. Причина не использовать его, потому что он расплывчатый . Не ясно, получаете ли вы мелкую или глубокую копию; это до исполнителя.

    Да, MemberwiseClone делает мелкую копию, но противоположность MemberwiseClone не Clone; это может быть, возможно, DeepClone, которого не существует. Когда вы используете объект через его интерфейс ICloneable, вы не можете знать, какой тип клонирования выполняет базовый объект. (И комментарии XML не прояснят это, потому что вы получите комментарии интерфейса, а не комментарии к методу Clone объекта. )

    Обычно я просто делаю метод Copy, который делает именно то, что я хочу.

    ответ дан Ryan Lundy, с репутацией 152014, 17.09.2008
  • 0 рейтинг

    Хотя стандартная практика заключается в реализации интерфейса ICloneable (описан здесь , так что я не буду срыгивать), вот хороший копир глубокого объекта-клона, который я нашел Кодовый проект некоторое время назад и включил в наш вещи.

    Как уже упоминалось, требуется, чтобы ваши объекты были сериализуемыми.

    using System;
    using System.IO;
    using System.Runtime.Serialization;
    using System.Runtime.Serialization.Formatters.Binary;
    
    /// 
    /// 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. ///
     
    public static class ObjectCopier
    {
        /// 
    /// Perform a deep Copy of the object. ///
     
        /// The type of object being copied.
        /// 
    The object instance to copy.
        /// The copied object.
        public static T Clone(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(this T source)
    {
       //...
    }
    

    Теперь вызов метода просто становится objectBeingCloned.Clone();.

    РЕДАКТИРОВАТЬ (10 января 2015 г.) Я подумал, что еще раз вернусь к этому, чтобы упомянуть, что недавно начал использовать (Newtonsoft) Json для этого, он должен быть на светлее на и избегать накладных расходов тегов [Serializable]. ( NB @atconway указал в комментариях, что частные члены не клонируются с использованием метода JSON)

    /// 
    /// Perform a deep Copy of the object, using Json as a serialisation method. NOTE: Private members are not cloned using this method. ///
     
    /// The type of object being copied.
    /// 
    The object instance to copy.
    /// The copied object.
    public static T CloneJson(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(JsonConvert.SerializeObject(source), deserializeSettings);
    }
    
    ответ дан johnc, с репутацией 18147, 17.09.2008
  • 0 рейтинг

    Я думаю, вы можете попробовать это.

    MyObject myObj = GetMyObj(); // Create and fill a new object
    MyObject newObj = new MyObject(myObj); //DeepClone it
    
    ответ дан Sudhanva Kotabagi, с репутацией 108, 19.08.2016
  • 0 рейтинг

    Я придумал это, чтобы преодолеть . NET Недостаток: необходимо вручную глубоко скопировать список & lt; T & gt ;.

    Я использую это:

    static public IEnumerable CloneList(List 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;
    }
    

    Я попытался придумать oneliner, который делает это, но это невозможно, так как yield не работает внутри блоков анонимного метода.

    Еще лучше, используйте общий список & lt; T & gt; клонер:

    class Utility where T : ICloneable
    {
        static public IEnumerable CloneList(List tl)
        {
            foreach (T t in tl)
            {
                yield return (T)t.Clone();
            }
        }
    }
    
    ответ дан Daniel Mošmondor, с репутацией 13932, 30.09.2009
  • 0 рейтинг

    В. Почему я выбрал этот ответ?

    • Выберите этот ответ, если вы хотите самую быструю скорость. NET способен на.
    • Не обращайте внимания на этот ответ, если вам нужен действительно простой метод клонирования.

    Другими словами, идет с другим ответом, если у вас нет узкого места в производительности, которое нужно исправить, и вы можете доказать это с помощью профилировщика .

    В 10 раз быстрее, чем другие методы

    Следующий метод выполнения глубокого клона:

    • в 10 раз быстрее, чем все, что связано с сериализацией / десериализацией;
    • Довольно чертовски близко к теоретической максимальной скорости. NET способен на.

    и метод. , ,

    Для максимальной скорости вы можете использовать Nested MemberwiseClone для создания глубокой копии . Это почти такая же скорость, как при копировании структуры значений, и намного быстрее, чем (а) отражение или (б) сериализация (как описано в других ответах на этой странице).

    Обратите внимание, что , если вы используете Nested MemberwiseClone для глубокой копии , вы должны вручную реализовать ShallowCopy для каждого вложенного уровня в классе и DeepCopy, который вызывает все упомянутые методы ShallowCopy для создания полного клона. Это просто: всего несколько строк, см. Демонстрационный код ниже.

    Вот выходные данные кода, показывающего относительную разницу в производительности для 100 000 клонов:

    • 1. 08 секунд для вложенного MemberwiseClone на вложенных структурах
    • 4. 77 секунд для вложенного MemberwiseClone на вложенных классах
    • 39. 93 секунд для сериализации / десериализации

    Использование Nested MemberwiseClone в классе почти так же быстро, как копирование структуры, а копирование структуры - чертовски близко к теоретической максимальной скорости. 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
    

    Чтобы понять, как сделать глубокое копирование с помощью MemberwiseCopy, вот демонстрационный проект, который использовался для генерации времени выше:

    // Nested MemberwiseClone example. 
    // Added to demo how to deep copy a reference class.
    [Serializable] // Not required if using MemberwiseClone, only used for speed comparison using serialization.
    public class Person
    {
        public Person(int age, string description)
        {
            this.Age = age;
            this.Purchase.Description = description;
        }
        [Serializable] // Not required if using MemberwiseClone
        public class PurchaseType
        {
            public string Description;
            public PurchaseType ShallowCopy()
            {
                return (PurchaseType)this.MemberwiseClone();
            }
        }
        public PurchaseType Purchase = new PurchaseType();
        public int Age;
        // Add this if using nested MemberwiseClone.
        // This is a class, which is a reference type, so cloning is more difficult.
        public Person ShallowCopy()
        {
            return (Person)this.MemberwiseClone();
        }
        // Add this if using nested MemberwiseClone.
        // This is a class, which is a reference type, so cloning is more difficult.
        public Person DeepCopy()
        {
                // Clone the root ...
            Person other = (Person) this.MemberwiseClone();
                // ... then clone the nested class.
            other.Purchase = this.Purchase.ShallowCopy();
            return other;
        }
    }
    // Added to demo how to copy a value struct (this is easy - a deep copy happens by default)
    public struct PersonStruct
    {
        public PersonStruct(int age, string description)
        {
            this.Age = age;
            this.Purchase.Description = description;
        }
        public struct PurchaseType
        {
            public string Description;
        }
        public PurchaseType Purchase;
        public int Age;
        // This is a struct, which is a value type, so everything is a clone by default.
        public PersonStruct ShallowCopy()
        {
            return (PersonStruct)this;
        }
        // This is a struct, which is a value type, so everything is a clone by default.
        public PersonStruct DeepCopy()
        {
            return (PersonStruct)this;
        }
    }
    // Added only for a speed comparison.
    public class MyDeepCopy
    {
        public static T DeepCopy(T 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(Bob);
                total += BobsSon.Age;
            }
            Console.Write("  Elapsed time: {0},{1}\n", sw.Elapsed, total);
        }
        Console.ReadKey();
    }
    

    Опять же, обратите внимание, что , если вы используете Nested MemberwiseClone для глубокой копии , вы должны вручную реализовать ShallowCopy для каждого вложенного уровня в классе и DeepCopy, который вызывает все упомянутые методы ShallowCopy для создания полного клона. Это просто: всего несколько строк, см. Демонстрационный код выше.

    Типы значений против Типы ссылок

    Обратите внимание, что когда дело доходит до клонирования объекта, существует большая разница между « struct » и « class »:

    • Если у вас есть « struct », это тип значения , так что вы можете просто скопировать его, и его содержимое будет клонировано (но оно будет делать только мелкий клон, если вы не будете использовать методы в этом посте).
    • Если у вас есть « класс », это ссылочный тип , поэтому, если вы копируете его, все, что вы делаете, это копируете указатель на него. Чтобы создать настоящий клон, вы должны быть более креативными и использовать различия между типами значений и ссылками типов , что создает в памяти еще одну копию исходного объекта.

    См. различия между типами значений и ссылочными типами .

    Контрольные суммы для помощи в отладке

    • Неправильное клонирование объектов может привести к очень сложным ошибкам. В рабочем коде я склонен реализовывать контрольную сумму, чтобы дважды проверить, что объект был клонирован правильно и не был поврежден из-за другой ссылки на него. Эта контрольная сумма может быть отключена в режиме деблокирования.
    • Я нахожу этот метод весьма полезным: часто вам нужно только клонировать части объекта, а не все.

    Действительно полезен для отделения многих потоков от многих других потоков

    Одним из отличных вариантов использования этого кода является подача клонов вложенного класса или структуры в очередь для реализации шаблона производитель / потребитель.

    • У нас может быть один (или несколько) потоков, которые изменяют принадлежащий им класс, а затем помещают полную копию этого класса в ConcurrentQueue.
    • Затем у нас есть один (или более) поток, извлекающий копии этих классов и работающий с ними.

    На практике это работает очень хорошо и позволяет нам отделить множество потоков (производителей) от одного или нескольких потоков (потребителей).

    И этот метод слишком быстр: если мы используем вложенные структуры, это в 35 раз быстрее, чем сериализация / десериализация вложенных классов, и позволяет нам использовать все потоки, доступные на машине.

    Обновление

    Очевидно, что ExpressMapper работает быстрее, если не быстрее, чем ручное кодирование, как описано выше. Возможно, мне придется посмотреть, как они сравниваются с профилировщиком.

    ответ дан Contango, с репутацией 38504, 4.07.2015
  • 0 рейтинг

    Я создал версию принятого ответа, которая работает как с [[Serializable]], так и с [DataContract]. Прошло много времени с тех пор, как я написал это, но, если я правильно помню, [DataContract] нуждался в другом сериализаторе.

    Требуется Система, Система. IO, Система. Продолжительность. Сериализация, Система. Продолжительность. Сериализация. Форматтеры. Бинарная система. Xml ;

    public static class ObjectCopier
    {
    
        /// 
    /// Perform a deep Copy of an object that is marked with '[Serializable]' or '[DataContract]' ///
     
        /// The type of object being copied.
        /// 
    The object instance to copy.
        /// The copied object.
        public static T Clone(T source)
        {
            if (typeof(T).IsSerializable == true)
            {
                return CloneUsingSerializable(source);
            }
    
            if (IsDataContract(typeof(T)) == true)
            {
                return CloneUsingDataContracts(source);
            }
    
            throw new ArgumentException("The type must be Serializable or use DataContracts.", "source");
        }
    
    
        /// 
    /// Perform a deep Copy of an object that is marked with '[Serializable]' ///
     
        /// 
        /// 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
        /// 
        /// The type of object being copied.
        /// 
    The object instance to copy.
        /// The copied object.
        public static T CloneUsingSerializable(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);
            }
        }
    
    
        /// 
    /// Perform a deep Copy of an object that is marked with '[DataContract]' ///
     
        /// The type of object being copied.
        /// 
    The object instance to copy.
        /// The copied object.
        public static T CloneUsingDataContracts(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);
                    }
                }
            }
        }
    
    
        /// 
    /// Helper function to check if a class is a [DataContract] ///
     
        /// 
    The type of the object to check.
        /// Boolean flag indicating if the class is a DataContract (true) or not (false) 
        public static bool IsDataContract(Type type)
        {
            object[] attributes = type.GetCustomAttributes(typeof(DataContractAttribute), false);
            return attributes.Length == 1;
        }
    
    } 
    
    ответ дан Jeroen Ritmeijer, с репутацией 2475, 11.04.2014
  • 0 рейтинг

    Если вы хотите истинное клонирование неизвестных типов, вы можете взглянуть на fastclone .

    Это клонирование на основе выражений, работающее примерно в 10 раз быстрее, чем двоичная сериализация, и поддерживающее полную целостность графов объектов.

    Это означает: если вы несколько раз ссылаетесь на один и тот же объект в вашей иерархии, клон также будет иметь ссылку на один экземпляр.

    Нет необходимости в интерфейсах, атрибутах или любых других модификациях клонируемых объектов.

    ответ дан Michael Sander, с репутацией 2011, 16.02.2015
  • 0 рейтинг

    Расширение C #, которое будет поддерживать типы «not ISerializable ».

     public static class AppExtensions
     {                                                                      
           public static T DeepClone(this T a)
           {
               using (var stream = new MemoryStream())
               {
                   var serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
    
                   serializer.Serialize(stream, a);
                   stream.Position = 0;
                   return (T)serializer.Deserialize(stream);
               }
           }                                                                    
     }
    

    Использование

           var obj2 = obj1.DeepClone()
    
    ответ дан Sameera R., с репутацией 2528, 3.05.2018
  • 0 рейтинг

    Невероятно, сколько усилий вы можете потратить с интерфейсом IClonable, особенно если у вас тяжелая иерархия классов. Также MemberwiseClone работает как-то странно - он точно не клонирует даже обычные структуры типа List.

    И, конечно, наиболее интересной дилеммой для сериализации является сериализация обратных ссылок - e. г. иерархии классов, где у вас есть дочерние и родительские отношения. Я сомневаюсь, что двоичный сериализатор сможет помочь вам в этом случае. (Это закончится рекурсивными циклами + переполнение стека).

    Мне как-то понравилось решение, предложенное здесь: Как сделать глубокую копию объекта в NET (C # конкретно)?

    однако - он не поддерживал списки, добавил, что поддержка, также принял во внимание переопределение. Для родительских прав только правило, которое я сделал для этого поля или свойства, должно называться «родительским», тогда оно будет игнорироваться DeepClone. Возможно, вы захотите определить свои собственные правила для обратных ссылок - для древовидных иерархий это может быть «влево / вправо» и т. Д. , ,

    Вот весь фрагмент кода, включая тестовый код:

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    
    namespace TestDeepClone
    {
        class Program
        {
            static void Main(string[] args)
            {
                A a = new A();
                a.name = "main_A";
                a.b_list.Add(new B(a) { name = "b1" });
                a.b_list.Add(new B(a) { name = "b2" });
    
                A a2 = (A)a.DeepClone();
                a2.name = "second_A";
    
                // Perform re-parenting manually after deep copy.
                foreach( var b in a2.b_list )
                    b.parent = a2;
    
    
                Debug.WriteLine("ok");
    
            }
        }
    
        public class A
        {
            public String name = "one";
            public List list = new List();
            public List null_list;
            public List b_list = new List();
            private int private_pleaseCopyMeAsWell = 5;
    
            public override string ToString()
            {
                return "A(" + name + ")";
            }
        }
    
        public class B
        {
            public B() { }
            public B(A _parent) { parent = _parent; }
            public A parent;
            public String name = "two";
        }
    
    
        public static class ReflectionEx
        {
            public static Type GetUnderlyingType(this MemberInfo member)
            {
                Type type;
                switch (member.MemberType)
                {
                    case MemberTypes.Field:
                        type = ((FieldInfo)member).FieldType;
                        break;
                    case MemberTypes.Property:
                        type = ((PropertyInfo)member).PropertyType;
                        break;
                    case MemberTypes.Event:
                        type = ((EventInfo)member).EventHandlerType;
                        break;
                    default:
                        throw new ArgumentException("member must be if type FieldInfo, PropertyInfo or EventInfo", "member");
                }
                return Nullable.GetUnderlyingType(type) ?? type;
            }
    
            /// 
    /// Gets fields and properties into one array. /// Order of properties / fields will be preserved in order of appearance in class / struct. (MetadataToken is used for sorting such cases) ///
     
            /// 
    Type from which to get
            /// array of fields and properties
            public static MemberInfo[] GetFieldsAndProperties(this Type type)
            {
                List fps = new List();
                fps.AddRange(type.GetFields());
                fps.AddRange(type.GetProperties());
                fps = fps.OrderBy(x => x.MetadataToken).ToList();
                return fps.ToArray();
            }
    
            public static object GetValue(this MemberInfo member, object target)
            {
                if (member is PropertyInfo)
                {
                    return (member as PropertyInfo).GetValue(target, null);
                }
                else if (member is FieldInfo)
                {
                    return (member as FieldInfo).GetValue(target);
                }
                else
                {
                    throw new Exception("member must be either PropertyInfo or FieldInfo");
                }
            }
    
            public static void SetValue(this MemberInfo member, object target, object value)
            {
                if (member is PropertyInfo)
                {
                    (member as PropertyInfo).SetValue(target, value, null);
                }
                else if (member is FieldInfo)
                {
                    (member as FieldInfo).SetValue(target, value);
                }
                else
                {
                    throw new Exception("destinationMember must be either PropertyInfo or FieldInfo");
                }
            }
    
            /// 
    /// Deep clones specific object. /// Analogue can be found here: https://stackoverflow.com/questions/129389/how-do-you-do-a-deep-copy-an-object-in-net-c-specifically /// This is now improved version (list support added) ///
     
            /// 
    object to be cloned
            /// full copy of object.
            public static object DeepClone(this object obj)
            {
                if (obj == null)
                    return null;
    
                Type type = obj.GetType();
    
                if (obj is IList)
                {
                    IList list = ((IList)obj);
                    IList newlist = (IList)Activator.CreateInstance(obj.GetType(), list.Count);
    
                    foreach (object elem in list)
                        newlist.Add(DeepClone(elem));
    
                    return newlist;
                } //if
    
                if (type.IsValueType || type == typeof(string))
                {
                    return obj;
                }
                else if (type.IsArray)
                {
                    Type elementType = Type.GetType(type.FullName.Replace("[]", string.Empty));
                    var array = obj as Array;
                    Array copied = Array.CreateInstance(elementType, array.Length);
    
                    for (int i = 0; i < array.Length; i++)
                        copied.SetValue(DeepClone(array.GetValue(i)), i);
    
                    return Convert.ChangeType(copied, obj.GetType());
                }
                else if (type.IsClass)
                {
                    object toret = Activator.CreateInstance(obj.GetType());
    
                    MemberInfo[] fields = type.GetFieldsAndProperties();
                    foreach (MemberInfo field in fields)
                    {
                        // Don't clone parent back-reference classes. (Using special kind of naming 'parent' 
                        // to indicate child's parent class.
                        if (field.Name == "parent")
                        {
                            continue;
                        }
    
                        object fieldValue = field.GetValue(obj);
    
                        if (fieldValue == null)
                            continue;
    
                        field.SetValue(toret, DeepClone(fieldValue));
                    }
    
                    return toret;
                }
                else
                {
                    // Don't know that type, don't know how to clone it.
                    if (Debugger.IsAttached)
                        Debugger.Break();
    
                    return null;
                }
            } //DeepClone
        }
    
    }
    
    ответ дан TarmoPikaro, с репутацией 1531, 24.04.2015
  • 0 рейтинг

    Мне нравятся такие конструкторы копирования:

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

    Если у вас есть какие-то вещи, чтобы скопировать, добавьте их

    ответ дан LuckyLikey, с репутацией 1103, 6.03.2015
  • 0 рейтинг

    Я видел, что это также реализовано с помощью отражения. По сути, существовал метод, который будет перебирать членов объекта и соответствующим образом копировать их в новый объект. Когда он достиг ссылочных типов или коллекций, я думаю, что он сделал рекурсивный вызов сам по себе. Отражение дорого, но сработало довольно хорошо.

    ответ дан xr280xr, с репутацией 5896, 19.10.2010
  • 0 рейтинг

    Сохраняйте простоту и используйте AutoMapper , как уже упоминалось, это простая маленькая библиотека для сопоставления одного объекта другому. , , Чтобы скопировать объект в другой объект того же типа, все, что вам нужно, это три строки кода:

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

    Целевой объект теперь является копией исходного объекта. Не достаточно просто? Создайте метод расширения для использования везде в вашем решении:

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

    При использовании метода расширения три строки становятся одной строкой:

    MyType copy = source.Copy();
    
    ответ дан Stacked, с репутацией 3224, 28.05.2016
  • 0 рейтинг

    Я только что создал CloneExtensions Библиотека проекта Он выполняет быстрое глубокое клонирование с использованием простых операций присваивания, генерируемых компиляцией кода среды выполнения Expression Tree.

    Как его использовать?

    Вместо того, чтобы писать свои собственные методы Clone или Copy с тоном назначений между полями и свойствами, заставьте программу сделать это самостоятельно, используя Дерево выражений. Метод GetClone(), помеченный как метод расширения, позволяет вам просто вызвать его в своем экземпляре:

    var newInstance = source.GetClone();
    

    Вы можете выбрать, что следует скопировать из source в newInstance, используя CloningFlags enum:

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

    Что можно клонировать?

    • Примитив (int, uint, byte, double, char и т. Д. ), известный неизменным типы (DateTime, TimeSpan, String) и делегаты (включая Action, Func и т. Д.)
    • Nullable
    • T [] массивов
    • Пользовательские классы и структуры, включая общие классы и структуры.

    Следующие члены класса / структуры внутренне клонированы:

    • Значения общедоступных, не читаемых только полей
    • Значения общедоступных свойств с методами доступа get и set
    • Элементы коллекции для типов, реализующих ICollection

    Как быстро это?

    Решение быстрее, чем отражение, поскольку информация об участниках должна быть собрана только один раз, прежде чем GetClone впервые будет использован для данного типа T.

    Это также быстрее, чем решение на основе сериализации, когда вы клонируете более пары экземпляров одного типа T.

    и более. , ,

    Подробнее о сгенерированных выражениях см. В документации .

    Пример списка отладки выражений для List:

    .Lambda #Lambda1(
        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.Collections.Generic.List`1[System.Int32]))
            ) {
                $target = (System.Collections.Generic.List`1[System.Int32]).Call ($initializers.Item[.Constant(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(Fields)) == (System.Byte).Constant(Fields)
            ) {
                .Default(System.Void)
            } .Else {
                .Default(System.Void)
            };
            .If (
                ((System.Byte)$flags & (System.Byte).Constant(Properties)) == (System.Byte).Constant(Properties)
            ) {
                .Block() {
                    $target.Capacity = .Call CloneExtensions.CloneFactory.GetClone(
                        $source.Capacity,
                        $flags,
                        $initializers)
                }
            } .Else {
                .Default(System.Void)
            };
            .If (
                ((System.Byte)$flags & (System.Byte).Constant(CollectionItems)) == (System.Byte).Constant(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))
            target = (List)initializers[typeof(List)].Invoke((object)source);
        else
            target = new List();
    
        if((flags & CloningFlags.Properties) == CloningFlags.Properties)
        {
            target.Capacity = target.Capacity.GetClone(flags, initializers);
        }
    
        if((flags & CloningFlags.CollectionItems) == CloningFlags.CollectionItems)
        {
            var targetCollection = (ICollection)target;
            foreach(var item in (ICollection)source)
            {
                targetCollection.Add(item.Clone(flags, initializers));
            }
        }
    
        return target;
    }
    

    Разве это не похоже на то, как вы бы написали свой собственный метод Clone для List?

    ответ дан MarcinJuraszek, с репутацией 106420, 24.12.2013
  • 0 рейтинг

    Генератор кода

    Мы увидели много идей от сериализации до ручной реализации и отражения, и я хочу предложить совершенно другой подход с использованием генератора кода CGbR . Метод генерирования клона эффективен с точки зрения памяти и процессора и поэтому в 300 раз быстрее, чем стандартный DataContractSerializer.

    Все, что вам нужно, это частичное определение класса с ICloneable, а генератор сделает все остальное:

    public partial class Root : ICloneable
    {
        public Root(int number)
        {
            _number = number;
        }
        private int _number;
    
        public Partial[] Partials { get; set; }
    
        public IList 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(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;
        }
    }
    

    Примечание: Последняя версия имеет более пустые проверки, но я оставил их для лучшего понимания.

    ответ дан Toxantron, с репутацией 1625, 9.06.2016
  • 0 рейтинг

    Если ваше дерево объектов является сериализуемым, вы также можете использовать что-то вроде этого

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

    следует знать, что это решение довольно просто, но оно не так эффективно, как другие решения.

    И будьте уверены, что если класс будет расти, все равно будут клонированы только те поля, которые также будут сериализованы.

    ответ дан LuckyLikey, с репутацией 1103, 20.04.2015
  • 0 рейтинг

    Ну, у меня были проблемы с использованием ICloneable в Silverlight, но мне понравилась идея разделения, я могу отделить XML, поэтому я сделал это:

    static public class SerializeHelper
    {
        //Michael White, Holly Springs Consulting, 2009
        //michael@hollyspringsconsulting.com
        public static T DeserializeXML(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 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 objClone) where T:new()
        {
            string GetString = SerializeHelper.SeralizeObjectToXML(objClone);
            return SerializeHelper.DeserializeXML(GetString);
        }
    }
    
    ответ дан Michael White, с репутацией 289, 2.12.2009
  • 0 рейтинг

    Лучше всего реализовать метод расширения , например

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

    , а затем использовать его в любом месте решения по

    var copy = anyObject.DeepClone();
    

    Мы можем иметь следующие три реализации:

    1. По сериализации (самый короткий код)
    2. По отражению - В 5 раз быстрее
    3. По деревьям выражений - 20x быстрее

    Все связанные методы хорошо работают и были тщательно протестированы.

    ответ дан frakon, с репутацией 783, 3.08.2016
  • 0 рейтинг

    Для клонирования вашего объекта класса вы можете использовать Object. MemberwiseClone метод,

    просто добавьте эту функцию в свой класс:

    public class yourClass
    {
        // ...
        // ...
    
        public yourClass DeepCopy()
        {
            yourClass othercopy = (yourClass)this.MemberwiseClone();
            return othercopy;
        }
    }
    

    затем, чтобы выполнить глубокую независимую копию, просто вызовите метод DeepCopy:

    yourClass newLine = oldLine.DeepCopy();
    

    надеюсь, что это помогает.

    ответ дан Chtiwi Malek, с репутацией 6275, 25.04.2014
  • 0 рейтинг

    Поскольку я не смог найти клонер, который соответствует всем моим требованиям в разных проектах, я создал глубокий клонер, который можно настраивать и адаптировать к различным структурам кода, вместо того, чтобы адаптировать мой код для удовлетворения требований к клонерам. Это достигается путем добавления аннотаций к коду, который должен быть клонирован, или вы просто оставляете код так, как он имеет поведение по умолчанию. Он использует рефлексы, кеширование типов и основан на более быстрых . Процесс клонирования очень быстрый для огромного количества данных и высокой иерархии объектов (по сравнению с другими алгоритмами, основанными на отражении / сериализации).

    https: // github. com / kalisohn / CloneBehave

    Также доступен как пакет nuget: https: // www. NuGet. орг / пакеты / Clone. Ведите / 1. 0. 0

    Например: следующий код будет DeepClone Address, но будет выполнять только поверхностную копию поля _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
    
    ответ дан kalisohn, с репутацией 211, 25.01.2016
  • 0 рейтинг

    Выполните следующие действия:

    • Определите ISelf со свойством только для чтения Self, которое возвращает T, и ICloneable, которое наследуется от ISelf и включает метод T Clone().
    • Затем определите тип CloneBase, который реализует приведение типа 26404225281 MemberwiseClone к переданному типу.
    • Каждый производный тип должен реализовывать VirtualClone, вызывая базовый метод клонирования и затем делая все необходимое для правильного клонирования тех аспектов производного типа, которые родительский метод VirtualClone еще не обработал.

    Для максимальной универсальности наследования классы, представляющие открытую функциональность клонирования, должны быть sealed, но наследоваться от базового класса, который в остальном идентичен, за исключением отсутствия клонирования. Вместо того, чтобы передавать переменные явно клонируемого типа, возьмите параметр типа ICloneable. Это позволит подпрограмме, которая ожидает, что клонируемое производное Foo будет работать с клонируемым производным DerivedFoo, но также позволит создавать неклонируемые производные Foo.

    ответ дан supercat, с репутацией 54774, 7.12.2011
  • 0 рейтинг

    Здесь быстрое и простое решение, которое сработало для меня, не опираясь на сериализацию / десериализацию.

    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();
    }
    
    ответ дан Daniele D., с репутацией 1781, 29.07.2016
  • 0 рейтинг

    Еще один JSON. Чистый ответ. Эта версия работает с классами, которые не поддерживают ISerializable.

    public static class Cloner
    {
        public static T Clone(T source)
        {
            if (ReferenceEquals(source, null))
                return default(T);
    
            var settings = new JsonSerializerSettings { ContractResolver = new ContractResolver() };
    
            return JsonConvert.DeserializeObject(JsonConvert.SerializeObject(source, settings), settings);
        }
    
        class ContractResolver : DefaultContractResolver
        {
            protected override IList CreateProperties(Type type, MemberSerialization memberSerialization)
            {
                var props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                    .Select(p => base.CreateProperty(p, memberSerialization))
                    .Union(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                        .Select(f => base.CreateProperty(f, memberSerialization)))
                    .ToList();
                props.ForEach(p => { p.Writable = true; p.Readable = true; });
                return props;
            }
        }
    }
    
    ответ дан Matthew Watson, с репутацией 70234, 14.03.2018
  • 0 рейтинг

    Я нашел новый способ сделать это, Emit.

    Мы можем использовать Emit, чтобы добавить IL в приложение и запустить его. Но я не думаю, что это хороший способ, потому что я хочу улучшить это, когда я пишу свой ответ.

    Эмит может видеть официальный документ и Руководство

    Вы должны изучить некоторый IL, чтобы прочитать код. Я напишу код, который может скопировать свойство в классе.

    public static class Clone
    {        
        // ReSharper disable once InconsistentNaming
        public static void CloneObjectWithIL(T source, T los)
        {
            //see http://lindexi.oschina.io/lindexi/post/C-%E4%BD%BF%E7%94%A8Emit%E6%B7%B1%E5%85%8B%E9%9A%86/
            if (CachedIl.ContainsKey(typeof(T)))
            {
                ((Action) CachedIl[typeof(T)])(source, los);
                return;
            }
            var dynamicMethod = new DynamicMethod("Clone", null, new[] { typeof(T), typeof(T) });
            ILGenerator generator = dynamicMethod.GetILGenerator();
    
            foreach (var temp in typeof(T).GetProperties().Where(temp => temp.CanRead && temp.CanWrite))
            {
                //do not copy static that will except
                if (temp.GetAccessors(true)[0].IsStatic)
                {
                    continue;
                }
    
                generator.Emit(OpCodes.Ldarg_1);// los
                generator.Emit(OpCodes.Ldarg_0);// s
                generator.Emit(OpCodes.Callvirt, temp.GetMethod);
                generator.Emit(OpCodes.Callvirt, temp.SetMethod);
            }
            generator.Emit(OpCodes.Ret);
            var clone = (Action) dynamicMethod.CreateDelegate(typeof(Action));
            CachedIl[typeof(T)] = clone;
            clone(source, los);
        }
    
        private static Dictionary CachedIl { set; get; } = new Dictionary();
    }
    

    Код может быть глубокой копией, но он может копировать свойство. Если вы хотите сделать его для глубокого копирования, вы можете изменить его для IL, это слишком сложно, поэтому я не могу это сделать.

    ответ дан lindexi, с репутацией 1849, 8.08.2017
  • 0 рейтинг

    Простой метод расширения для копирования всех открытых свойств. Работает для любых объектов и не требует , класс должен быть [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 ) } );
            }
        };
    }
    
    ответ дан Konstantin Salavatov, с репутацией 2879, 16.03.2011
  • 0 рейтинг

    Если вы уже используете стороннее приложение, такое как ValueInjecter или Automapper , вы можете сделать что-то вроде этого:

    MyObject oldObj; // The existing object to clone
    
    MyObject newObj = new MyObject();
    newObj.InjectFrom(oldObj); // Using ValueInjecter syntax
    

    Используя этот метод, вам не нужно реализовывать ISerializable или ICloneable на ваших объектах. Это часто встречается в паттерне MVC / MVVM, поэтому были созданы простые инструменты, подобные этому.

    , см. , значение injcter для решения проблемы глубокого клонирования на CodePlex .

    ответ дан Michael Cox, с репутацией 1083, 15.10.2012
  • 0 рейтинг

    После большого прочтения о многих из опций, связанных здесь, и возможных решениях этой проблемы, я считаю, что все опции суммированы довольно хорошо в , ссылка Яна Р (все другие варианты - варианты тех) и Лучшее решение предоставлено по ссылке Pedro77 на комментарии к вопросу.

    Так что я просто скопирую соответствующие части этих двух ссылок здесь. Таким образом, мы можем иметь:

    Лучшее, что нужно сделать для клонирования объектов в диез!

    Прежде всего, это все наши варианты:

    Статья Fast Deep Copy по деревьям выражений также имеет сравнение производительности клонирования по деревьям сериализации, отражения и выражения.

    Почему я выбираю ICloneable (т.е. е. вручную)

    Г-н Венкат Субраманиам (избыточная ссылка здесь) подробно объясняет, почему .

    Вся его статья обводится вокруг примера, который пытается быть применимым в большинстве случаев, используя 3 объекта: Человек , Мозг и Город . Мы хотим клонировать человека, у которого будет свой мозг, но тот же город. Вы можете изобразить все проблемы, которые могут принести другие методы, приведенные выше, или прочитать статью.

    Это моя слегка измененная версия своего заключения:

    Копирование объекта путем указания New, за которым следует имя класса, часто приводит к тому, что код не является расширяемым. Использование клона, применение шаблона прототипа, является лучшим способом для достижения этой цели. Однако использование клона, как это предусмотрено в C # (и Java), также может быть довольно проблематичным. Лучше предоставить защищенный (непубличный) конструктор копирования и вызвать его из метода clone. Это дает нам возможность делегировать задачу создания объекта экземпляру самого класса, обеспечивая таким образом расширяемость, а также безопасное создание объектов с помощью конструктора защищенной копии.

    Надеемся, что эта реализация прояснит ситуацию:

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

    Теперь рассмотрим класс, производный от Person.

    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
    

    Заметьте, что если мы будем вести подсчет количества объектов, то клон, реализованный здесь, будет вести правильный подсчет количества объектов.

    ответ дан cregox, с репутацией 10444, 26.09.2012
  • 0 рейтинг

    как насчет просто переделать внутри метода это должно вызвать в основном конструктор автоматического копирования

    T t = new T();
    T t2 = (T)t;  //eh something like that
    
            List cloneum;
            public void SomeFuncB(ref List _mylist)
            {
                cloneum = new List();
                cloneum = (List < myclass >) _mylist;
                cloneum.Add(new myclass(3));
                _mylist = new List();
            }
    

    мне кажется работает

    ответ дан will_m, с репутацией 11, 13.04.2014
  • 0 рейтинг

    Картограф выполняет глубокое копирование. Каждый член вашего объекта создает новый объект и присваивает все его значения. Он работает рекурсивно на каждом не примитивном внутреннем элементе.

    Предлагаю вам один из самых быстрых, в настоящее время активно развивающихся. Я предлагаю UltraMapper https: // github. com / maurosampietro / UltraMapper

    Nuget пакетов: https: // www. NuGet. org / пакеты / UltraMapper /

    ответ дан sam, с репутацией 1648, 23.04.2017
  • 0 рейтинг

    Этот метод решил проблему для меня:

    private static MyObj DeepCopy(MyObj source)
            {
    
                var DeserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };
    
                return JsonConvert.DeserializeObject(JsonConvert.SerializeObject(source), DeserializeSettings);
    
            }
    

    Используйте это так: MyObj a = DeepCopy(b);

    ответ дан JerryGoyal, с репутацией 9110, 12.04.2016
  • 0 рейтинг

    Это скопирует все доступные для чтения и записи свойства объекта другому.

     public class PropertyCopy 
                            where TSource: class, new()
                            where TTarget: class, new()
            {
                public static TTarget Copy(TSource src, TTarget trg, params string[] properties)
                {
                    if (src==null) return trg;
                    if (trg == null) trg = new TTarget();
                    var fulllist = src.GetType().GetProperties().Where(c => c.CanWrite && c.CanRead).ToList();
                    if (properties != null && properties.Count() > 0)
                        fulllist = fulllist.Where(c => properties.Contains(c.Name)).ToList();
                    if (fulllist == null || fulllist.Count() == 0) return trg;
    
                    fulllist.ForEach(c =>
                        {
                            c.SetValue(trg, c.GetValue(src));
                        });
    
                    return trg;
                }
            }
    

    и вот как вы его используете:

     var cloned = Utils.PropertyCopy.Copy(_tmp, dbsave,
                                                                "Creation",
                                                                "Description",
                                                                "IdTicketStatus",
                                                                "IdUserCreated",
                                                                "IdUserInCharge",
                                                                "IdUserRequested",
                                                                "IsUniqueTicketGenerated",
                                                                "LastEdit",
                                                                "Subject",
                                                                "UniqeTicketRequestId",
                                                                "Visibility");
    

    или скопировать все:

    var cloned = Utils.PropertyCopy.Copy(_tmp, dbsave);
    
    ответ дан Ylli Prifti, с репутацией 86, 8.08.2013
  • 0 рейтинг

    Я хотел клонер для очень простых объектов, в основном из примитивов и списков. Если ваш объект из коробки JSON сериализуем, то этот метод поможет. Это не требует модификации или реализации интерфейсов клонированного класса, просто сериализатор JSON, такой как JSON. СЕТЬ.

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

    Кроме того, вы можете использовать этот метод расширения

    public static class SystemExtension
    {
        public static T Clone(this T source)
        {
            var serialized = JsonConvert.SerializeObject(source);
            return JsonConvert.DeserializeObject(serialized);
        }
    }
    
    ответ дан craastad, с репутацией 2998, 3.04.2013
  • 0 рейтинг

    В общем, вы реализуете интерфейс ICloneable и внедряете Clone самостоятельно. Объекты C # имеют встроенный метод MemberwiseClone, который выполняет поверхностное копирование, которое может помочь вам для всех примитивов.

    Для глубокой копии нет способа узнать, как автоматически это сделать.

    ответ дан HappyDude, с репутацией 1192, 17.09.2008
  • 0 рейтинг

    Хорошо, в этом посте есть очевидный пример с отражением, НО отражение обычно медленное, пока вы не начнете его правильно кэшировать.

    , если вы кешируете его правильно, то он клонирует 1000000 объектов на 4,6 с (измерено Watcher).

    static readonly Dictionary ProperyList = new Dictionary();
    

    , чем вы берете кэшированные свойства или добавляете новые в словарь и используете их просто

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

    полный код проверки в моем посте в другом ответе

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

    ответ дан Roma Borodov, с репутацией 286, 19.12.2015
  • 0 рейтинг
    1. В основном вам нужно реализовать интерфейс ICloneable, а затем реализовать копирование структуры объекта.
    2. Если это полная копия всех участников, вам нужно убедиться (не относясь к выбранному вами решению), что все дети также являются клонируемыми.
    3. Иногда вам нужно знать о некоторых ограничениях во время этого процесса, например, если вы копируете объекты ORM, большинство структур допускает только один объект, присоединенный к сеансу, и вы НЕ ДОЛЖНЫ создавать клоны этого объекта, или если это возможно, вам нужно заботиться о сессионном прикреплении этих объектов.

    ура.

    ответ дан dimarzionist, с репутацией 8941, 17.09.2008
  • 0 рейтинг

    Вот глубокая копия реализации:

    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;
    }
    
    ответ дан dougajmcdonald, с репутацией 12594, 6.09.2011
  • 0 рейтинг

    Я предпочитаю конструктор копирования клону. Намерение яснее.

    ответ дан Nick, с репутацией 9309, 17.09.2008
  • 0 рейтинг

    Я знаю, что этот вопрос и ответ сидят здесь некоторое время, и следование - не совсем ответ, а скорее наблюдение, на которое я недавно натолкнулся, когда проверял, действительно ли рядовые не клонируются (я бы не был сам, если бы У меня нет;) когда я с радостью скопирую @johnc обновленный ответ .

    Я просто сделал сам метод расширения (который в значительной степени вставлен в копию вышеупомянутого ответа):

    public static class CloneThroughJsonExtension
    {
        private static readonly JsonSerializerSettings DeserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };
    
        public static T CloneThroughJson(this T source)
        {
            return ReferenceEquals(source, null) ? default(T) : JsonConvert.DeserializeObject(JsonConvert.SerializeObject(source), DeserializeSettings);
        }
    }
    

    и упал наивный класс, как это (на самом деле их было больше, но они не связаны):

    public class WhatTheHeck
    {
        public string PrivateSet { get; private set; } // matches ctor param name
    
        public string GetOnly { get; } // matches ctor param name
    
        private readonly string _indirectField;
        public string Indirect => $"Inception of: {_indirectField} "; // matches ctor param name
        public string RealIndirectFieldVaule => _indirectField;
    
        public WhatTheHeck(string privateSet, string getOnly, string indirect)
        {
            PrivateSet = privateSet;
            GetOnly = getOnly;
            _indirectField = indirect;
        }
    }
    

    и код как это:

    var clone = new WhatTheHeck("Private-Set-Prop cloned!", "Get-Only-Prop cloned!", "Indirect-Field clonned!").CloneThroughJson();
    Console.WriteLine($"1. {clone.PrivateSet}");
    Console.WriteLine($"2. {clone.GetOnly}");
    Console.WriteLine($"3.1. {clone.Indirect}");
    Console.WriteLine($"3.2. {clone.RealIndirectFieldVaule}");
    

    привело к:

    1. Private-Set-Prop cloned!
    2. Get-Only-Prop cloned!
    3.1. Inception of: Inception of: Indirect-Field cloned!
    3.2. Inception of: Indirect-Field cloned!
    

    Я был цельным, как: Что Ф. , , поэтому я схватил Ньютонсофт. Json Github репо и начал копать. В результате получается, что при десериализации типа, который имеет только один ctor и совпадают его имена параметров ( без учета регистра ), имена открытых свойств будут передаваться в ctor как эти параметры. Некоторые подсказки можно найти в коде здесь и здесь .

    Итог

    Я знаю, что это довольно необычный случай, и пример кода немного оскорбительный, но эй! Это застало меня врасплох, когда я проверял, есть ли дракон, ожидающий в кустах, чтобы выскочить и укусить меня в задницу. ;)

    ответ дан gaa, с репутацией 891, 1.02.2017
  • 0 рейтинг

    Краткий ответ: вы наследуете от интерфейса ICloneable, а затем реализуете. функция клона. Клон должен сделать для каждого члена копию и выполнить глубокое копирование любого члена, который в этом нуждается, а затем вернуть полученный объект. Это рекурсивная операция (она требует, чтобы все члены класса, который вы хотите клонировать, были либо типами значений, либо реализовали ICloneable, и чтобы их члены были либо типами значений, либо реализовали ICloneable и т. Д.).

    Для более подробного объяснения клонирования с использованием ICloneable, просмотрите эту статью .

    Длинный длинный ответ "это зависит". Как уже упоминалось, ICloneable не поддерживается обобщениями, требует особых соображений для циклических ссылок на классы и фактически рассматривается некоторыми как «ошибка» в. NET Framework. Метод сериализации зависит от того, ваши объекты сериализуемы, а они могут отсутствовать, и вы не можете контролировать их. В сообществе все еще много споров о том, что является «лучшей» практикой. На самом деле, ни одно из решений не является универсальным, подходящим для всех лучших практик для всех ситуаций, в которых ICloneable изначально интерпретировался.

    См. Статью «Уголок разработчика» за , где приведены еще несколько опций (благодарность Иану).

    ответ дан Zach Burlingame, с репутацией 10218, 17.09.2008