Быстрее глубокое клонирование - PullRequest
21 голосов
/ 12 мая 2009

Кто-нибудь хочет каркас / класс, который позволяет мне клонировать по значениям .Net объектов? Меня интересуют только общедоступные свойства чтения / записи (а именно DataContracts), и мне все равно, правильно ли разрешаются ссылки (то есть, коллекции, которые дважды содержат один и тот же экземпляр элемента).

Я попробовал трюк сериализации с помощью DataContractSerializer (сериализация в XML и обратно), написал класс клонирования на основе отражений (иногда быстрее / иногда медленнее), и мне было интересно, если кто-то написал вспомогательный класс, который может сделать это через Emit, а не отражение. На данный момент испускание IL немного для моего маленького мозга, но я думаю, что это было бы окончательным решением. Если кто-то не знает альтернативный метод, который быстрее, чем DataContractSerializer.

Ответы [ 9 ]

21 голосов
/ 13 июня 2012

Некоторое время назад я написал три метода глубокого клонирования для .NET:

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

  • Для второго я использовал чистое отражение. Это было как минимум в 6 раз быстрее, чем клонирование с BinaryFormatter. Этот также можно использовать в Silverlight и .NET Compact Framework.

  • Третий использует деревья выражений Linq (для генерации MSIL во время выполнения). Это в 60 раз быстрее, чем метод BinaryFormatter, но время установки каждого класса в первый раз составляет приблизительно 2 миллисекунды.

Logarithmic scale illustrating cloning performance

Я опубликовал все три метода клонирования как Open Source здесь:

http://blog.nuclex -games.com / моно-DotNet / быстро глубоководного Клонирование /

14 голосов
/ 12 мая 2009

Если вы говорите о дереве / графике объектов:

Написание конкретного IL для сериализации объекта довольно сложно. ИМО, лучше всего посмотреть на полную сериализацию, например, как будет работать DataContractSerializer - но не обязательно с этим движком.

Например, protobuf-net имеет метод Serializer.DeepClone<T>, который может помочь. Это должно быть быстрее, чем DataContractSerializer, по крайней мере. В настоящее время вам необходимо добавить некоторые подсказки для сериализатора (даже если просто [ProtoContract(ImplicitFields=ImplicitFields.AllPublic)]) - однако текущая (неполная) незавершенная работа предлагает поддержку POCO без атрибутов.


Если вы говорите об отдельных объектах:

Есть довольно простые вещи, которые вы можете сделать здесь с помощью Expression в .NET 3.5; построить динамический Expression на основе отражения и вызвать .Compile(). MiscUtil уже имеет это:

DestType clone = PropertyCopy<DestType>.CopyFrom(original);

В .NET 2.0 / 3.0 (без Expression) вы можете рассмотреть HyperDescriptor для аналогичных целей.

5 голосов
/ 09 ноября 2016

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

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

Кроме того, @frakon упоминает, что деревья выражений имеют ту же скорость, что и IL Emit, это немного некорректно. Дерево выражений немного медленнее, но его можно использовать в частично доверенном приложении.

Ручной 13мс

DeepCloner (IL Emit) 167 мс

DeepCloner (выражения) 267мс

CloneExtensions (выражения) 560ms

NClone 901ms

Clone.Behave! 8551ms

GeorgeCloney 1996ms

Nuclex. Клонирование не доступно (разбился)

FastDeepCloner 1882мс

Бинарный формат 15000мс

4 голосов
/ 12 июля 2016

Вероятно, в Интернете нет полного рабочего кода клонирования, созданного IL Emit.

Но IL Emit имеет ту же скорость, что и код для деревьев выражений, потому что оба метода заканчиваются похожими функциями скомпилированного лямбда-копирования. Деревья выражений примерно в 4 раза быстрее, чем в Reflection . Лучше всего то, что общая функция клонирования Expression Trees доступна в Интернете .

Одна реализация в деревьях выражений уже упоминалась Cygon. Новую тщательно протестированную реализацию можно найти в статье CodeProject. Быстрое глубокое копирование по деревьям выражений (C #) .

Использовать метод расширения

var copy = originalObject.DeepCopyByExpressionTree();
3 голосов
/ 12 мая 2009

Я не знаю, соответствует ли это вашим требованиям, но вы также можете создать глубокий клон, используя BinaryFormatter. См. этот ответ на связанный вопрос (от Binoj Antony ):

public static class GenericCopier<T>
{
    public static T DeepCopy(object objectToCopy)
    {
        using (MemoryStream memoryStream = new MemoryStream())
        {
            BinaryFormatter binaryFormatter = new BinaryFormatter();
            binaryFormatter.Serialize(memoryStream, objectToCopy);
            memoryStream.Seek(0, SeekOrigin.Begin);
            return (T) binaryFormatter.Deserialize(memoryStream);
        }
    }
}
3 голосов
/ 12 мая 2009

Попробуйте AutoMapper или BLToolkit Mapping

1 голос
/ 12 мая 2009

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

Вы можете сделать 1 метод для свойства / поля или один метод для всего объекта. Из моих сравнительных тестов выполнение 1 для свойства не дает вам слишком большой удар по производительности.

См. Следующий код, чтобы увидеть, как я это делаю в медиабраузере: http://code.google.com/p/videobrowser/source/browse/trunk/MediaBrowser/Library/Persistance/Serializer.cs

Там также есть несколько юнит-тестов.

Существует быстрый пример отражения на пределе команд, который делает именно то, что вы хотите.

см .:

http://theinstructionlimit.com/?p=76

0 голосов
/ 20 сентября 2016

Хорошо! Вы можете написать свой собственный метод клонирования, который вы можете указать, чтобы игнорировать или включать свойства по своим атрибутам. Моя новая библиотека в ссылке вниз использует отражение и FieldInfo для рекурсивного клонирования объекта. я добавил его в CodeProject, чтобы вы вскоре получили доступ к его коду, который вы можете изменить в соответствии со своими потребностями.

Попробуйте его очень быстро и чисто, вам понравится.
https://www.nuget.org/packages/FastDeepCloner/1.0.1
или же
PM> Install-Package FastDeepCloner

0 голосов
/ 09 июня 2016

Генератор кода CGbR может создать для вас реализацию ICloneable. Все, что вам нужно, это пакет nuget и частичное определение класса, которое реализует ICloneable. Генератор сделает все остальное за вас:

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

    public Partial[] Partials { get; set; }

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

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

    private Root()
    {
    }
} 

public partial class Root
{
    public Root Clone(bool deep)
    {
        var copy = new Root();
        // All value types can be simply copied
        copy._number = _number; 
        if (deep)
        {
            // In a deep clone the references are cloned 
            var tempPartials = new Partial[Partials.Length];
            for (var i = 0; i < Partials.Length; i++)
            {
                var value = Partials[i];
                value = value.Clone(true);
                tempPartials[i] = value;
            }
            copy.Partials = tempPartials;
            var tempNumbers = new List<ulong>(Numbers.Count);
            for (var i = 0; i < Numbers.Count; i++)
            {
                var value = Numbers[i];
                tempNumbers[i] = value;
            }
            copy.Numbers = tempNumbers;
        }
        else
        {
            // In a shallow clone only references are copied
            copy.Partials = Partials; 
            copy.Numbers = Numbers; 
        }
        return copy;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...