Установка значений в производном классе из конструктора базового класса с помощью Reflection - PullRequest
3 голосов
/ 21 июля 2011

У меня есть два класса, как это:

public abstract class MyBase
{
     protected MyBase(){
         Initialize();
     }

     protected IDictionary<string,string> _data;

     private void Initialize() {
         // Use Reflection to get all properties
         // of the derived class (e.g., call new MyDerived() then
         // I want to know the names "Hello" and "ID" here
         var data = GetDataFromBackend(propertyNamesFromDerived);
         _data = data;
     }
}

public class MyConcrete : MyBase
{
     public MyConcrete(){
         // Possibly use Reflection here
         Hello = _data["Hello"];
         ID = new Guid(data["ID"]);
     }

     public string Hello {get;set;}
     public Guid ID {get; set;}
}

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

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

У меня есть бэкэнд-система, в которой хранятся пары ключ / значение, по сути, Dictionary<string,string>. Я хочу абстрагироваться от работы с этой серверной системой таким образом, чтобы люди могли создавать классы, свойства которых являются ключами в серверной системе. Когда они конструируют этот объект, он автоматически загружает данные из этой системы и инициализирует в нем все переменные.

Другими словами, я только что заново изобрел сериализацию, за исключением того, что я не контролирую бэкэнд-систему и просто делаю работу с ней действительно безболезненной. Я не хочу, чтобы вызывающие абоненты вызывали Initialize() после создания объекта, потому что в 100% случаев вы должны инициализировать его после создания.

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

Должен ли я использовать Фабрику? Или считается безопасным смотреть на свойство names производного класса в базовом конструкторе? (Не заботьтесь об их значениях и о том, что они не инициализированы, просто нужны имена).

Или есть ли лучший способ создать фасад между Словарём строк и конкретным бизнес-объектом?

Редактировать: Это .net 3.5, поэтому нет System.Dynamic, который сделал бы это тривиальным: (

Редактировать 2: После того, как я посмотрел на Ответы и еще раз обдумал это, я думаю, что мой вопрос действительно сводится к следующему: вызывается GetType (). GetProperties () из базового конструктора по порядку получить имен свойств и если они украшены определенным сейфом атрибута?

Ответы [ 6 ]

5 голосов
/ 21 июля 2011

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

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

T Get<T>() where T : new()

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

var concrete = foo.Get<MyConcrete>();

Это не сложно, и это правильный способ сделать это.

Кстати, код для Get будет выглядеть примерно так:

T t = new T();
var properties = typeof(T).GetProperties();
foreach(var property in properties) {
    property.SetValue(t, dictionary[property.Name], null);
}
return t;

, где dictionary - ваши загруженные пары ключ / значение.Оказывается, есть более оптимальные способы сделать это, но если бы это не было узким местом, я бы об этом не беспокоился.

3 голосов
/ 21 июля 2011

Лучший способ сделать это - заставить классы напрямую использовать словарь:

public string Hello {
    get { return (string)base.data["Hello"]; }
    set { base.data["Hello"] = value; }
}

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

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

Если вы не хотите делать это таким образом, вы можете вызвать GetType().GetProperties(), чтобы получить PropertyInfo объектов для свойств в вашем классе, а затем вызвать SetValue(this, value).
Это будет медленно; Существуют различные приемы, которые можно использовать для ускорения работы с помощью деревьев выражений, CreateDelegate или генерации IL.

1 голос
/ 21 июля 2011

Возможно попробуйте Template method pattern

0 голосов
/ 22 июля 2011

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

0 голосов
/ 21 июля 2011

Я не уверен, действительно ли вы должны это делать, но вот что вы просили (укажите это в Initialize, и вы получите список имен производных свойств):

        var derivedProps = this.GetType().GetProperties();
        var propNames = new List<string>(derivedProps.Select(x => x.Name));

Оттуда, используя PropertyInfo s в derivedProps, вы можете установить свойства.

0 голосов
/ 21 июля 2011

Рассматривали ли вы использование ExpandoObject ? С его помощью вы можете динамически добавлять свойства и проверять их (например, при сериализации).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...