Копировать свойства объекта: отражение или сериализация - что быстрее? - PullRequest
9 голосов
/ 18 ноября 2011

У меня есть два объекта одного типа, и мне нужно скопировать значения свойств из одного объекта в другой. Есть два варианта:

  1. Использовать отражение, перемещаться по свойствам первого объекта и копировать значения.

  2. Сериализация первого объекта и десериализация копии.

Оба работают для моего требования, вопрос в том, что мне лучше использовать в терминах скорости (стоимости)?

Пример

class Person
{
    public int ID { get; set; }
    public string Firsthand { get; set; } 
    public string LastName { get; set; } 
    public int Age { get; set; } 
    public decimal Weight { get; set; } 
}

Необходимо скопировать значения свойств из Person p1 в Person p2.

Для этого простого примера - какой метод быстрее?

Обновление

Для сериализации я использую предложенный здесь ObjectCopier: Глубокое клонирование объектов

Для отражения я использую этот код:

foreach (PropertyInfo sourcePropertyInfo in copyFromObject.GetType().GetProperties())  
{
    PropertyInfo destPropertyInfo = copyToObject.GetType().GetProperty(sourcePropertyInfo.Name);

    destPropertyInfo.SetValue(
        copyToObject,
        sourcePropertyInfo.GetValue(copyFromObject, null),
        null);
}

Ответы [ 5 ]

10 голосов
/ 18 ноября 2011

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

Edit # 1: Насколько я знаю, BinaryFormatter используемый вашим классом использует рефлексию для выполнения своей работы.Итак, вопрос в том, можете ли вы написать (быстрее?) Пользовательский код отражения для ваших типов, чем Microsoft для общего сценария?

Edit # 2: Из любопытства я запустил простойтестовое задание.BinaryFormatter против отражения с точки зрения выполнения мелкая копия .Код отражения, который я использовал, можно увидеть здесь:

var newPerson = Activator.CreateInstance<Person>();
var fields = newPerson.GetType().GetFields(BindingFlags.Public 
    | BindingFlags.Instance);
foreach (var field in fields)
{
    var value = field.GetValue(person);
    field.SetValue(newPerson, value);
}

Каковы результаты по сравнению с классом ObjectCopier, который вы используете?Отражение, кажется, работает в 7 раз быстрее , чем код сериализации.Однако это относится к классу Person с открытыми полями .Для свойств разница все еще заметна, но она только в 2 раза быстрее.

Я предполагаю, что разница заключается в том, что BinaryFormatter необходимо использовать потоки, которые вносят дополнительные издержки.Тем не менее, это только мое предположение, которое может быть далеко от фактов.

Исходный код для используемой мной программы тестирования можно найти здесь .Любой может указать на недостатки и возможные проблемы с этим: -)


Sidenote
Как и все "Мне было интересно ..." тесты, я предлагаю вам взять его с зерном соли.Такие оптимизации должны быть сделаны только тогда, когда их производительность действительно становится проблемой.

8 голосов
/ 18 ноября 2011

В конечном счете, сериализаторы общего назначения (такие как BinaryFormatter, через ObjectCopier) используют отражение . То, как хорошо они используют его, зависит от конкретного сериализатора, но при сериализации всегда возникают дополнительные издержки.

Поскольку вам нужна только мелкая копия, такой инструмент, как AutoMapper, является наиболее подходящим инструментом здесь; опять же, он использует рефлексию (но я ожидаю, что он делает это «правильным образом», то есть не через GetValue() / SetValue()), но у него нет затрат на сериализацию.

В этом сценарии сериализация излишня; AutoMapper вполне разумно. Если вы хотите глубокие клоны, это становится сложнее ... сериализация может начать заманчиво. Я все еще, вероятно, не выбрал бы BinaryFormatter сам, но я очень привередлив в сериализации; p

Конечно, было бы тривиально направить некоторое основное отражение, которое делает то же самое с помощью GetValue() и т. Д., Но это было бы медленно. Еще один интересный вариант заключается в том, что вы можете использовать API Expression для создания копировщика объектов во время выполнения ... но ... AutoMapper делает все, что вам нужно, поэтому это кажется излишним усилием.

0 голосов
/ 10 августа 2017
void Copy(object copyToObject, object copyFromObject)
{
    BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;

    FieldInfo[] fields = copyFromObject.GetType().GetFields(flags);
    for (int i = 0; i < fields.Length; ++i)
    {
        BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic
            | BindingFlags.Static;
        FieldInfo field = copyFromObject.GetType().GetField(fields[i].Name, bindFlags);
        FieldInfo toField = copyToObject.GetType().GetField(fields[i].Name, bindFlags);
        if(field != null)
        {
            toField.SetValue(copyToObject, field.GetValue(copyFromObject));
        }
    }
}
0 голосов
/ 18 ноября 2011

Если вы копируете свойства во время выполнения, ответом будет отражение. Я бы пошел на сериализацию, если бы не во время выполнения. Сериализация против отражения посмотрите на это один раз.

0 голосов
/ 18 ноября 2011

Двоичная сериализация выполняется очень быстро. Я часто использую ее для решения подобных проблем

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

...