Подтвердить что ты не робот

Более быстрое глубокое клонирование

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

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

4b9b3361

Ответ 1

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

Написание определенного 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 для аналогичных целей.

Ответ 2

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

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

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

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

Logarithmic scale illustrating cloning performance

Горизонтальная ось показывает количество клонированных объектов (хотя каждый клонированный объект включает в себя несколько вложенных объектов).

BinaryFormatter помечен как "Сериализация" на графике. Ряд данных "Отражение" является пользовательским, который копирует поля с помощью GetField()/SetField().

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

http://blog.nuclex-games.com/mono-dotnet/fast-deep-cloning/

Ответ 3

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

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

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

Ручной 13мс

DeepCloner (IL Emit) 167 мс

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

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

NClone 901мс

Clone.Behave! 8551ms

GeorgeCloney 1996ms

Nuclex.Cloning нет (разбился)

FastDeepCloner 1882ms

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

Ответ 4

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

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

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

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

var copy = originalObject.DeepCopyByExpressionTree();

Ответ 6

Я не знаю, соответствует ли это вашим требованиям точно, но вы также можете создать глубокий клон, используя 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);
        }
    }
}

Ответ 7

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

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

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

Там также есть некоторые модульные тесты.

Есть образец быстрого отражения на границе раздела, который делает именно то, что вы хотите.

см.:

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

Ответ 8

Генератор кодов 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;
    }
}

Ответ 9

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

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