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

Как использовать отражение для упрощения конструкторов и сравнений?

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

public Foo(Foo other)
{
    this.Bar = other.Bar;
    this.Baz = other.Baz;
    this.Lur = other.Lur;
    this.Qux = other.Qux;
    this.Xyzzy= other.Xyzzy;
}

Действительно, это просто развернутый цикл, который выполняет итерации через свойства, копируя их между объектами. Так почему бы не быть честным в этом факте? Отражение на помощь!

public Foo(IFoo other)
{
    foreach (var property in typeof(IFoo).GetProperties())
    {
        property.SetValue(this, property.GetValue(other, null), null);
    }
}

Я могу просто попытаться заставить парадигму, которую я узнал от Lua на С#, но этот конкретный пример не кажется мне слишком вонючим. Отсюда я начал делать более сложные вещи, которые были чувствительны к порядку полей. Например, вместо того, чтобы иметь стек практически идентичных операторов if для создания строки из полей, я просто перебираю их в нужном порядке:

public override string ToString()
{
    var toJoin = new List<string>();
    foreach (var property in tostringFields)
    {
        object value = property.GetValue(this, null);
        if (value != null)
            toJoin.Add(value.ToString());
    }
    return string.Join(" ", toJoin.ToArray());
}
private static readonly PropertyInfo[] tostringFields =
{
    typeof(IFoo).GetProperty("Bar"),
    typeof(IFoo).GetProperty("Baz"),
    typeof(IFoo).GetProperty("Lur"),
    typeof(IFoo).GetProperty("Qux"),
    typeof(IFoo).GetProperty("Xyzzy"),
};

Итак, теперь у меня есть итеративность, которую я хотел, но у меня все еще есть стеки кода, отражающие каждое свойство, которое меня интересует (я также делаю это для CompareTo, используя другой набор свойств в другом порядке). Хуже того, это потеря сильной типизации. Это действительно начинает пахнуть.

Ну а как насчет использования атрибутов для каждого свойства для определения порядка? Я начал по этой дороге, и действительно, она работала хорошо, но это просто заставило все выглядело раздутым. Он отлично работает семантически, но я всегда опасаюсь использовать расширенные функции только потому, что они "опрятные". Использует ли рефлексия таким образом избыток? Есть ли еще какое-то другое решение проблемы с левым/правым кодом, которую я пропускаю?

4b9b3361

Ответ 1

Использование рефлексии само по себе неплохо, но вы получите удар производительности, особенно если вы это сделаете рекурсивно.

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

Существуют и другие способы достижения того, что вы хотите, в том числе Marc Gravells "Дескриптор свойств Hyper" , или если вы хотите узнать некоторые IL и OPCodes, вы может использовать System.Reflection.Emit или даже Cecil from Mono.

Вот пример использования дескриптора Hyper Property Descriptor, который вы можете настроить для своих нужд:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using Hyper.ComponentModel;
namespace Test {
    class Person {
        public int Id { get; set; }
        public string Name { get; set; }
    }
    class Program {
        static void Main() {
            HyperTypeDescriptionProvider.Add(typeof(Person));
            var properties = new Dictionary<string, object> { { "Id", 10 }, { "Name", "Fred Flintstone" } };
            Person person = new Person();
            DynamicUpdate(person, properties);
            Console.WriteLine("Id: {0}; Name: {1}", person.Id, person.Name);
            Console.ReadKey();
        }
        public static void DynamicUpdate<T>(T entity, Dictionary<string, object>  {
            foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(typeof(T)))
                if (properties.ContainsKey(propertyDescriptor.Name))
                    propertyDescriptor.SetValue(entity, properties[propertyDescriptor.Name]);
        }
    }
}

Если вы решите продолжить использование рефлексии, вы можете снизить производительность, кэшируя свои вызовы GetProperties() следующим образом:

public Foo(IFoo other) {
    foreach (var property in MyCacheProvider.GetProperties<IFoo>())
        property.SetValue(this, property.GetValue(other, null), null);
}

Ответ 2

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

Библиотека называется AutoMapper и отображает ее из одного объекта в другой и делает это путем динамического создания сборки IL на лету, Это гарантирует, что, кроме первого удара, вы получите превосходную производительность, и ваш код будет намного проще:

public Foo(Foo other)
{
    Mapper.Map(other, this);
}

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

Я провел некоторое тестирование производительности, и после первого попадания 20 мс (все еще довольно быстро) он приближался к 0, как вы можете получить. Довольно впечатляет.

Надеюсь, это поможет кому-то.

Ответ 3

IMHO, отражение - очень мощная функция С#, но которая, скорее всего, приведет к раздутому коду и значительно добавит к кривой обучения кода и уменьшит ремонтопригодность. Вероятнее всего, вы будете совершать ошибки (когда базовый рефакторинг может привести к ошибкам) ​​и больше боится изменить имя любого свойства (если вам посчастливилось найти лучшее имя) или тому подобное.

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

Ответ 4

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

На самом деле нет необходимости в каких-либо фантазии. Если вы хотите иметь возможность итерировать свойства, вы можете использовать Map < > в качестве хранилища резервных копий для всех свойств вашего класса.

Кстати, именно так мастер проекта VS подразумевает настройки приложения для вас. (см. System.Configuration.ApplicationSettingsBase). Он также очень похож на lua-like

   public bool ConfirmSync {
        get {
            return ((bool)(this["ConfirmSync"]));
        }
        set {
            this["ConfirmSync"] = value;
        }
    }