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

Похоже, вам нужно установить ссылку на:

glfw.lib 
opengl32.lib

. Стоит прочитать примечания к выпуску: здесь

2088
задан poke 16 December 2015 в 09:42
поделиться

10 ответов

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

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

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

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

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

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

идея состоит в том, что это сериализирует Ваш объект и затем десериализовывает его в новый объект. Преимущество - то, что Вы не должны интересоваться о клонировании всего, когда объект становится слишком сложным.

И с использованием дополнительных методов (также из источника, на который первоначально ссылаются):

В случае, если Вы предпочитаете использовать новое дополнительные методы из C# 3.0, измените метод для имения следующей подписи:

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

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

РЕДАКТИРОВАНИЕ (10 января 2015) Мысль я пересмотрел бы это, чтобы упомянуть, что я недавно начал использовать (Newtonsoft) Json, чтобы сделать это, это должно быть легче и избегает издержек [сериализуемых] тегов. ( 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);
}
1628
ответ дан 20 revs, 14 users 64% 16 December 2015 в 09:42
поделиться
  1. В основном необходимо реализовать интерфейс ICloneable и затем понять копирование структуры объекта.
  2. , Если это - глубокая копия всех участников, необходимо обеспечить (не имеющий отношение на решении, которое Вы выбираете), что все дети являются клонируемыми также.
  3. Иногда необходимо знать о некотором ограничении во время этого процесса, например, если Вы копирующий ORM возражаете, что большинство платформ позволяет только один объект, присоединенный к сессии, и Вы не ДОЛЖНЫ делать клоны этого объекта, или если возможно, что необходимо заботиться о присоединении сессии этих объектов.

Аплодисменты.

17
ответ дан Christian Davén 16 December 2015 в 09:42
поделиться
  • 1
    Сегодня, это - лучшее решение, чем все вышеупомянутое. – Cezar 13 March 2014 в 15:34

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

81
ответ дан Peter Mortensen 16 December 2015 в 09:42
поделиться
  • 1
    @Pete, Вы имели какой-либо конкретный продукт в виду? – Thorbjørn Ravn Andersen 4 June 2010 в 12:22

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

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

8
ответ дан HappyDude 16 December 2015 в 09:42
поделиться
  • 1
    onClosed обесцениваемый используйте afterClose – Ajmal Salim 4 June 2013 в 18:00

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

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

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

169
ответ дан Ryan Lundy 16 December 2015 в 09:42
поделиться
  • 1
    сделал Вы знаете: запад/восточное побережье isn' t конец света! – Chris 4 June 2010 в 11:44

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

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

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

Посмотрите этот статья Corner Разработчика еще для нескольких опций (кредит Ian).

21
ответ дан Johann 16 December 2015 в 09:42
поделиться

Я придумал это, чтобы преодолеть недостаток .NET , когда нужно вручную копировать List .

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

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

И в другое место:

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

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

Еще лучше, используйте общий клонер List :

class Utility<T> where T : ICloneable
{
    static public IEnumerable<T> CloneList(List<T> tl)
    {
        foreach (T t in tl)
        {
            yield return (T)t.Clone();
        }
    }
}
11
ответ дан 22 November 2019 в 19:59
поделиться

Что ж, у меня были проблемы с использованием 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);
    }
}
32
ответ дан 22 November 2019 в 19:59
поделиться

Правовая оговорка: я - автор упомянутого пакета.

я был удивлен, как главные ответы на этот вопрос в 2019 все еще используют сериализацию или отражение.

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

BinaryFormatter, требует эти Serializable, атрибут, JsonConverter требует конструктора без параметров или атрибутов, никакой дескриптор поля только для чтения или взаимодействует через интерфейс очень хорошо, и оба 10-30x медленнее, чем необходимый.

Деревья выражений

можно вместо этого использовать Деревья выражений или Отражение. Испустите , чтобы сгенерировать клонирующийся код только однажды, затем использовать тот скомпилированный код вместо медленного отражения или сериализации.

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

можно найти проект на GitHub: https://использование github.com/marcelltoth/ObjectCloner

можно установить его от NuGet. Любой получает ObjectCloner пакет и использует его как:

var clone = ObjectCloner.DeepClone(original);

или если Вы не возражаете загрязнять свой тип объекта расширениями, добираются ObjectCloner.Extensions также и запись:

var clone = original.DeepClone();

Производительность

А простой сравнительный тест клонирования иерархии классов показал производительность ~3x быстрее, чем использование Отражения, ~12x быстрее, чем Newtonsoft. Сериализация Json и ~36x быстрее, чем высоко предложенный BinaryFormatter.

3
ответ дан 22 November 2019 в 19:59
поделиться

Самый короткий путь, но зависимость от потребности:

using Newtonsoft.Json;
    public static T Clone<T>(T source) =>
        JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source));
0
ответ дан 22 November 2019 в 19:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: