Питання Глибокі клонування об'єктів


Я хочу зробити щось на кшталт:

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

А потім внесіть зміни до нового об'єкту, які не відображені у вихідному об'єкті.

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

Як я можу клонувати або глибоко копіювати об'єкт так, щоб клонований об'єкт міг бути змінений без будь-яких змін, що відображаються у вихідному об'єкті?


1830
2017-09-17 00:06


походження


Може бути корисним: "Чому копіювання об'єкта - це жахлива річ?" agiledeveloper.com/articles/cloning072002.htm - Pedro77
stackoverflow.com/questions/8025890/... Інше рішення ... - Felix K.
Ви повинні подивитися на AutoMapper - Daniel Little
Ваше рішення набагато складніше, я втратив читати це ... hehehe. Я використовую інтерфейс DeepClone. відкритий інтерфейс IDeepCloneable <T> (T DeepClone (); } - Pedro77
@ Pedro77: турбота про мене IDeepCloneable полягає в тому, що не всі збірки посилань на речі, які можуть бути глибоко клонованими, мають бути; належна поведінка при клонуванні a List<T> залежить не тільки від цього T, але також і з метою списків. Якщо жоден із елементів у списках ніколи не піддасться будь-яким, що їх змінить, то навіть якщо елементи в списках можуть бути клоновані, то краще скопіювати посилання безпосередньо. - supercat


Відповіді:


Хоча стандартна практика полягає в реалізації 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();.

РЕДАГУВАТИ (10 січня 2015 р.) Думаю, я повернусь до цього, згадавши, що я нещодавно почав користуватися (Newtonsoft) Джонсом, щоб це зробити, це має бути легше і уникає накладних витрат на теги [Serializable]. (NB @atconway вказав у коментарі, що приватні учасники не клоновані, використовуючи метод JSON)

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

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

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

1466
2018-04-03 13:31



stackoverflow.com/questions/78536/cloning-objects-in-c/... має посилання на код вище [і посилання на дві інші такі реалізації, один з яких є більш підходящим у моєму контексті] - Ruben Bartelink
Серіалізація / десеріалізація передбачає значні накладні витрати, які не є необхідними. Див. Інтерфейс ICloneable і методи клонування .MemberWise () в C #. - 3Dave
@David надано, але якщо об'єкти є світлими, а продуктивність, що потрапляє при її використанні, не є занадто високою для ваших потреб, то це корисна порада. Я не використовував його інтенсивно з великою кількістю даних в циклі, я визнаю, але я ніколи не бачив жодного виступу. - johnc
@ Амір: насправді немає: typeof(T).IsSerializable також вірно, якщо тип позначений [Serializable] атрибут Це не потрібно реалізувати ISerializable інтерфейс - Daniel Gehriger
Просто думав, що я хотів би згадати, що, хоча цей метод є корисним, і я сам сам використовував його багато разів, він взагалі не сумісний із Medium Trust, тому стежте, чи ви пишете код, який потребує сумісності. BinaryFormatter отримує доступ до приватних полів і, таким чином, не може працювати за замовчуванням для приватних середовищ довіри. Ви можете спробувати інший serializer, але переконайтеся, що ваш абонент знає, що клон може бути не ідеальним, якщо вхідний об'єкт залежить від приватних полів. - Alex Norcliffe


Я хотів отримати клонер для дуже простих об'єктів, переважно примітивів та списків. Якщо ваш об'єкт поза серійним JSON коробки, то цей метод зробить трюк. Для цього не потрібно змінювати або застосовувати інтерфейси в клонованому класі, а просто JSON-серіалізатор, як JSON.NET.

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

183
2017-09-17 01:12



розв'язок навіть швидше, ніж рішення BinaryFormatter, Порівняння ефективності серіалізації. NET - esskar
Дякую за це. Я зміг зробити те ж саме з serializer BSON, який поставляється з драйвером MongoDB для C #. - Mark Ewer
Це найкращий спосіб для мене, однак я використовую Newtonsoft.Json.JsonConvert але це те ж саме - Pierre
Підхід JSON - це добре для невеликих плоских об'єктів, але оригінальне питання стосувалося будь-якого об'єкта, у тому числі глибоких. Серіалізатор NFX.Slim працює на порядок швидше на будь-якому .NET-типі, якщо у нього немає делегатів та некерованих покажчиків, ось джерело, яке працює так само, як BinaryFormnatter, принаймні в 5 разів швидше: github.com/aumcode/nfx/blob/master/Source/NFX/Serialization/...Тестовий набір: github.com/aumcode/serbench  доводить, що єдиним серіалістом, який швидше, є Protobuf, який не має типового динамізму та посилань - itadapter DKh
Для цього об'єкт для клонування повинен бути серійним, як вже згадувалося - це також означає, наприклад, що він може не мати кругових залежностей - radomeit


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

так, MemberwiseClone робить дрібну копію, але навпаки MemberwiseClone це не так Clone; це, можливо, буде DeepClone, якого не існує. Коли ви використовуєте об'єкт через інтерфейс ICloneable, ви не можете знати, який тип клонування виконує основний об'єкт. (І коментарі XML не будуть чіткі, оскільки ви отримаєте інтерфейсні коментарі, а не ті, що містяться в методі клонів об'єкта.)

Те, що я зазвичай роблю, це просто зробити Copy метод, який робить саме те, що я хочу.


147
2017-09-26 20:18



Я незрозуміло, чому ICloneable вважається нечітким. Отримавши такий тип, як Dictionary (Of T, U), я б очікував, що ICloneable.Clone повинен робити будь-який рівень глибокого та мілкого копіювання, необхідний для того, щоб новий словник був незалежним словником, який містить ті самі T та U (struct content, та / або посилання на об'єкт) як оригінал. Де неоднозначність? Звичайно, загальний ICloneable (Of T), який успадкував себе (Of T), який включав в себе метод "Self", був би набагато кращим, але я не бачу двозначності в глибокому та неглибокому клонування. - supercat
Ваш приклад ілюструє проблему. Припустимо, у вас є словник <string, Customer>. Якщо клонований словник повинен мати той же Об'єкти клієнта як оригінал, або копії з цих об'єктів Клієнта? Існують розумні випадки використання для кожного з них. Але ICloneable не дає зрозуміти, який ви отримаєте. Ось чому це не є корисним. - Ryan Lundy
@Kyralessa Стаття Microsoft MSDN насправді говорить про цю проблему, не знаючи, чи ви запитуєте глибоку або дрібну копію. - crush
Відповідь від дубліката stackoverflow.com/questions/129389/... описує розширення копіювання, засноване на рекурсивному члені Clone - Michael Freidgeim


На мій погляд, багато чого читаю про багато варіантів, зв'язаних тут, і з можливими рішеннями цієї проблеми всі варіанти дуже добре підсумовані Ян П.російська посилання (всі інші варіанти є варіаціями цих), і найкраще рішення забезпечується Педро77російська посилання на питання коментарів.

Тому я просто скопію відповідні частини цих 2 згадок тут. Таким чином ми можемо мати:

Найкраще, що потрібно зробити для клонування об'єктів c гострим!

Перш за все, це всі наші можливості:

The Стаття Швидке глибоке копіювання за допомогою виразних дерев   Має також порівняльне порівняння клонування Serialization, Reflection і Expression дерев.

Чому я вибираю ICloneable (тобто вручну)

Пан Венкат Субраманіам (надлишкове посилання тут) докладно пояснює, чому.

Вся його стаття описує приклад, який намагається застосовувати в більшості випадків, використовуючи 3 об'єкти: Особа, Мозок і Місто. Ми хочемо клонувати людину, яка матиме власний мозок, але це ж місто. Ви можете або зобразити всі проблеми будь-яким із наведених вище способів, щоб отримати або прочитати статтю.

Це моя трохи модифікована версія його висновку:

Копіювання об'єкта, вказавши його New за яким ім'я класу часто призводить до коду, який не розширюється. Використання клону, застосування прототипу, є кращим способом досягнення цього. Проте використання клону, як це передбачено в C # (та Java), також може бути досить проблематичним. Краще забезпечити захищений (непублічний) конструктор копій та викликати це з методу клонування. Це дає нам можливість делегувати завдання створення об'єкта до екземпляра самого класу, що забезпечує розширюваність, а також безпечне створення об'єктів за допомогою захищеного конструктора копій.

Сподіваємося, ця реалізація може зробити все чітке:

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

Тепер розглянемо, щоб клас був отриманий від Особи.

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

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

Ви можете спробувати запустити наступний код:

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

Випущена продукція буде:

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

Відзначимо, що якщо ми будемо враховувати кількість об'єктів, то клон, який реалізується тут, буде мати правильний підрахунок кількості об'єктів.


84
2017-09-17 00:13



MS рекомендує не використовувати ICloneable для громадських членів. "Оскільки виклики Clone не можуть залежати від способу, який виконує передбачувану операцію клонування, ми рекомендуємо, щоб ICloneable не було застосовано до загальнодоступних API." msdn.microsoft.com/en-us/library/... Однак, виходячи з пояснень, наданих Venkat Subramaniam у вашій пов'язаній статті, я думаю, що має сенс використовувати в цій ситуації поки творці ICnoneable об'єктів мають глибоке розуміння того, які властивості повинні бути глибокими або малими копіями (наприклад, глибоке копіювання мозку, мілководне копіювання міста) - BateTech
По-перше, я далеко від експерта в цій темі (публічні API). Я думай Одного разу це зауваження MS робить багато сенсу. І я не думаю, що це безпечно припустити користувачів цього API буде мати таке глибоке розуміння. Отже, має сенс реалізувати його лише на відкритий API якщо це дійсно не матиме значення для того, хто буде використовувати його. Я здогадатися маючи якийсь тип UML, що дуже чітко визначає різницю між кожним властивістю, може допомогти. Але я хотів би почути від когось з більшим досвідом. : П. - cregox
Ви можете скористатись Генератор клонів CGbR і отримати подібний результат, не вручну написавши код. - Toxantron
Середній рівень мовної реалізації є корисним - Michael Freidgeim


Я віддаю перевагу конструктору копії до клону. Намір ясніше.


69
2018-03-16 11:38



.Net не має конструкторів копій. - Pop Catalin
Звичайно, це: новий MyObject (objToCloneFrom) Просто оголосіть ctor, який приймає об'єкт для клонування як параметр. - Nick
Це не те ж саме. Ви повинні додати його до кожного класу вручну, і ви навіть не знаєте, чи гарантуєте ви глибоку копію. - Dave Van den Eynde
+1 для копіювання ctor. Ви також повинні вручну написати функцію клонів () для кожного типу об'єкта, і вдачі до цього, коли ієрархія класу отримує кілька рівнів глибини. - Andrew Grant
За допомогою конструкторів копії ви втрачаєте ієрархію, хоча. agiledeveloper.com/articles/cloning072002.htm - Will


Простий метод розширення для копіювання всіх загальнодоступних властивостей. Працює для будь-яких предметів і не вимагати класу бути [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 ) } );
        }
    };
}

36
2017-12-02 17:39



Це, на жаль, є недоліком. Це еквівалентно виклику objectOne.MyProperty = objectTwo.MyProperty (тобто він просто копіює посилання на). Це не буде клонувати значення властивостей. - Alex Norcliffe
до Алекса Норкліффа: автор питання запитував про "копіювання кожної власності", а не про клонування. в більшості випадків точне дублювання властивостей не потрібне. - Konstantin Salavatov
Я думаю про використання цього методу, але з рекурсією. тому, якщо значення властивості є посиланням, створіть новий об'єкт і знову натисніть CopyTo. Я просто бачу одну проблему, що всі використані класи повинні мати конструктор без параметрів. Хто-небудь намагався це вже? Я теж задаюсь питанням, чи дійсно це буде працювати з властивостями, що містять .net класи, як DataRow і DataTable? - Koryu


Ну, я мав проблеми з використанням ICloneable у Silverlight, але мені сподобалася ідея серальзації, я можу селенувати XML, тому я зробив це:

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

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

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

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

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

        return sbTR.ToString();
    }

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

28
2017-10-15 17:55





Якщо ви вже використовуєте сторонні програми, як-от ValueInjecter або Автомайстер, ви можете зробити щось на зразок цього:

MyObject oldObj; // The existing object to clone

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

За допомогою цього методу вам не потрібно реалізувати ISerializable або ICloneable на ваших об'єктах. Це поширене з моделлю MVC / MVVM, тому створені такі прості інструменти, як це.

побачити значення вектора глибокого клонування рішення на CodePlex.


26
2017-12-24 22:56





Я щойно створив CloneExtensions бібліотека проект Він виконує швидкий, глибокий клон за допомогою простих операцій присвоювання, створених за допомогою комбінації коду runtime дерева виразів.

Як його використовувати?

Замість того, щоб писати свої власні Clone або Copy методи з тоном розподілу між полями та властивостями роблять програму самостійно, використовуючи дерево виразів. GetClone<T>() Метод, позначений як метод розширення, дозволяє вам просто називати його на вашому прикладі:

var newInstance = source.GetClone();

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

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

Що можна клонувати?

  • Примітивні (int, uint, байт, подвійні, char і т. Д.), Відомі незмінними типи (DateTime, TimeSpan, String) та делегати (включаючи Дія, функція тощо)
  • Нульовий
  • T [] масиви
  • Спеціальні класи та структури, включаючи загальні класи та структури.

Наступні члени класу / struct клоновані внутрішньо:

  • Значення загальнодоступних, а не поля, що читаються
  • Значення загальнодоступних властивостей як з отриманими, так і з встановленими параметрами
  • Колекційні елементи для типів, що впроваджують ICotlection

Як швидко це?

Рішення швидше, ніж відбиття, тому що учасники інформації повинні збиратись лише один раз, раніше GetClone<T> використовується вперше для даного типу T.

Це також швидше, ніж рішення на основі серіалізації, якщо ви клонуватимете більше, ніж пару екземплярів того ж типу T.

і більше...

Дізнайтеся більше про згенеровані вирази на документація.

Вираз вираз для налагодження для List<int>:

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


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

}

що має таке ж значення, як наступний код #:

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


25
2017-09-17 00:14



Які шанси отримати це на NuGet? Це здається найкращим рішенням. Як це дорівнює? NClone? - crush
Я думаю, що дана відповідь повинна бути виправлена ​​ще раз. Вручну реалізація ICloneable - це утомительная і схильна до помилок, використовуючи відображення або серіалізацію повільно, якщо продуктивність є важливою і вам потрібно копіювати тисячі об'єктів протягом короткого періоду часу. - nightcoder
Не зовсім, ви помиляєтесь у рефлексії, ви просто повинні просто кешувати це правильно. Перевірте мою відповідь нижче stackoverflow.com/a/34368738/4711853 - Roma Borodov


Коротка відповідь - ви успадковуєте від інтерфейсу ICloneable, а потім реалізуєте функцію .clone. Клон повинен робити коректну копію і виконати глибокий копіювання для будь-якого члена, який потребує цього, а потім повернути отриманий об'єкт. Це рекурсивна операція (вона вимагає, щоб всі члени класу, який ви хочете клонувати, були або типом значення, або реалізували ICloneable, і що їх учасники є або типами значень, або реалізують ICloneable тощо).

Щоб отримати більш детальне пояснення щодо клонування за допомогою ICloneable, перевірте Ця стаття.

The довго відповідь "це залежить". Як згадували інші, ICloneable не підтримується генериками, вимагає спеціальних міркувань для циркулярних посилань на клас, і насправді їх розглядають деякі як "помилка" в .NET Framework. Метод серіалізації залежить від того, що ваші об'єкти є серіалізуючими, які вони можуть не бути, і ви, можливо, не матимете контролю над. В громаді все ще багато обговорень, про які йдеться про "найкращу" практику. Насправді, жоден з рішень не є єдиним для всіх найкращих практик у всіх ситуаціях, таких як ICloneable спочатку трактувався як такий.

Подивіться на це Стаття "Корпорація розробника" для ще декількох варіантів (кредит Яна).


20
2018-02-16 11:30



ICloneable не має загальний інтерфейс, тому не рекомендується використовувати цей інтерфейс. - Karg
Ваше рішення працює, поки він не буде обробляти циркулярні посилання, то все починає ускладнюватися, краще спробувати глибоке клонування, використовуючи глибоку серіалізацію. - Pop Catalin
На жаль, не всі об'єкти також можуть бути серійними, тому ви також не можете використовувати цей метод. Посилання Яна - це найповніша відповідь. - Zach Burlingame
+1, щоб згадати статтю Бред Абрамс. - Merlyn Morgan-Graham