C# копирование свойств с помощью reflection

164
15 февраля 2019, 10:20

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

public static class PropertyCopy<T> where T : new()
{
    private static readonly PropertyInfo[] _propertyInfo;
    static PropertyCopy()
    {
        _propertyInfo = typeof(T).GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance).Where(p => p.CanWrite).ToArray();
    }
    public static void CopyAllProperties(T from, T to)
    {
        foreach (var prop in _propertyInfo)
        {
            prop.SetValue(to, prop.GetValue(from));
        }
    }
}

Идея тут такая, когда впервые вызывается метод CopyAllProperties, в конструкторе создается массив со всеми необходимыми для копирования свойствами PropertyInfo[]. И при последующем вызове метода CopyAllProperties для этого типа массив создаваться уже не будет, все свойства уже выбраны и готовы в копированию. И по идее большая часть накладных расходов на производительность через reflection в данном случае снимается.

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

Этот код вполне работает и выглядит красиво.

Правильное ли предположение про "большая часть накладных расходов на производительность через reflection в данном случае снимается"?

Либо все таки вся проблема будет в prop.SetValue(to, prop.GetValue(from))?

Answer 1

Нет. Основные расходы на reflection случаются не в момент GetProperties (как раз метаданные средой выполнения кешируются), а при обращении к свойству через SetValue и GetValue.

Способ ускорения таких вещей известен - генерация кода на лету. Проще всего использовать Linq Expressions:

public static class PropertyCopy<T>
{
    private static readonly Action<T, T> copier;
    static PropertyCopy()
    {
        var p1 = Expression.Parameter(typeof(T), "from");
        var p2 = Expression.Parameter(typeof(T), "to");
        var props = from property in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
                    where property.CanRead && property.CanWrite
                    select Expression.Assign(Expression.Property(p2, property), Expression.Property(p1, property));
        copier = Expression.Lambda<Action<T, T>>(Expression.Block(props), p1, p2).Compile();
    }
    public static void CopyAllProperties(T from, T to) => copier(from, to);
}
READ ALSO
Оптимизация Content под разные разрешения Unity

Оптимизация Content под разные разрешения Unity

Имеется список кнопок, которые расположены одна под другой и не вмещающихся в видимую область на девайсахПри помощи Scroll Rect реализовано "проматывание"...

141
Не получается подключить библиотеку в Visual Stidio С#

Не получается подключить библиотеку в Visual Stidio С#

Доброго времени сутокСтолкнулся с проблемой:не могу подключить библиотеку в Visual Stidio

163
Как использовать метод в ViewModel?

Как использовать метод в ViewModel?

Есть приложение, в котором я хочу реализовать MVVMВ файле MainWindow

149
Ошибка Warning: mysqli_error() expects exactly 1 parameter, 0 given в запросе

Ошибка Warning: mysqli_error() expects exactly 1 parameter, 0 given в запросе

Помогите исправить ошибку в запросе:

149