Поля на лету от статического отражения?
Я изучал статическое отражение с помощью выражений LINQ - очень круто!
У меня была одна мысль - возможно ли, чтобы один класс "генерировал" поля в одном классе на основе статического отражения, сделанного в другом классе? Я специально думаю о модели Строителя, которую видел здесь много раз. Я хотел бы сделать регистрацию свойств в стиле беглого nhibernate, которая "генерирует" поля в компоновщике, которые соответствуют классу, который я хочу построить. Примерно так:
public class Color
{
private Color()
{
}
public string Name { get; private set; }
public class Builder : BuilderBase<Color>
{
public Builder()
{
Property(x => x.Name);
}
public Build()
{
return built_up_color;
}
}
}
и поддерживать синтаксис конструктора, как это:
Color c = new Color.Builder() { Name = "Red" }.Build();
Смысл всего этого состоит в том, чтобы уменьшить количество раз, которое я должен повторить, определяя свойства цвета. Я играл с этим:
public class Color
{
private Color()
{
}
public string Name { get; private set; }
public class Builder
{
private Color _color = new Color();
public string Name
{
get { return _color.Name; }
set { _color.Name = value; }
}
public Build()
{
return _color;
}
}
}
который, безусловно, работает и перечисляет свойства столько же раз, но выглядит более словесным и менее гибким. Похоже, я должен быть в состоянии сделать что-то анонимно-типа-у здесь?
3 ответа
Стоит отметить, что иметь класс с именем Color, конфликтующий с System.Drawing.Color, вероятно, плохая идея.
Это может привести к путанице в других (что еще хуже, System.Drawring.Color имеет семантику значений, в то время как ваш класс имеет семантику ссылок, которая может привести к дальнейшей путанице)
Я хотел бы отметить, что вы действительно хотите именованные необязательные аргументы. Я хотел бы предположить, что введение громоздких классов Builder теперь будет более трудоемким, и, как только вы доберетесь до C# 4.0, переходить к ним стало бы более болезненным. Вместо этого сделайте конструкторы, которые требуются (или, если необходимо, чтобы избежать коллизий сигнатур типов, статические фабричные методы в классе)
Я думаю, что это невозможно, вы не можете создавать участников, кроме как объявив их явно. Укусить пулю и объявить конструктор для Color
,
PS: я думаю, что статическое отражение является неправильным, единственное, что статично, это поиск члена, на которого вы хотите сослаться - хорошая вещь, насколько это возможно, но это не очень далеко.
Меньше кода для написания, но с использованием отражения для установки значений.
Хитрость заключается в использовании инициализаторов коллекции. это небезопасно.
public class Color
{
private Color()
{
}
public string Name { get; private set; }
public int Prop2 { get; private set; }
public class Builder : Builder<Color>
{
public Builder()
{
// possible
_instance.Name = "SomeDefaultValue";
}
}
}
class Builder<T> : IEnumerable<string>
{
protected T _instance = Activator.CreateInstance(typeof(T));
public void Add<TProperty>(Expression<Func<T, TProperty>> prop, TProperty value)
{
StaticReflection.GetPropertyInfo(prop).SetValue(_instance, value, null);
}
public static implicit operator T(Builder<T> builder)
{
return builder.Build();
}
public T Build()
{
return _instance;
}
IEnumerator<string> IEnumerable<string>.GetEnumerator()
{
// e.g. return iterator over the property names
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<string>)this).GetEnumerator();
}
}
и вызвать синтаксис
var color = new Color.Builder
{
{ x => x.Name, "foo" },
{ x => x.Prop2, 5 }
}.Build();
// or
var color = new Builder<Color>
{
{ x => x.Name, "foo" },
{ x => x.Prop2, 5 }
}.Build();
// or
Color color = new Builder<Color>
{
{ x => x.Name, "foo" },
{ x => x.Prop2, 5 }
};