Отражение и общие типы
Я пишу код для конструктора классов, который перебирает все свойства класса и вызывает общий статический метод, который заполняет мой класс данными из внешнего API. Итак, я получил это как пример класса:
public class MyClass{
public string Property1 { get; set; }
public int Property2 { get; set; }
public bool Property3 { get; set; }
public static T DoStuff<T>(string name){
// get the data for the property from the external API
// or if there a problem return 'default(T)'
}
}
Теперь в моем конструкторе я хочу что-то вроде этого:
public MyClass(){
var properties = this.GetType().GetProperties();
foreach(PropertyInfo p in properties){
p.SetValue(this, DoStuff(p.Name), new object[0]);
}
}
Таким образом, указанный выше конструктор выкинет ошибку, потому что я не поставляю родовой тип.
Итак, как мне передать тип свойства в?
Ответы
Ответ 1
Вы хотите вызвать DoStuff <T> с T = тип каждого свойства? В этом случае "как есть" вам нужно будет использовать отражение и MakeGenericMethod - i.e.
var properties = this.GetType().GetProperties();
foreach (PropertyInfo p in properties)
{
object value = typeof(MyClass)
.GetMethod("DoStuff")
.MakeGenericMethod(p.PropertyType)
.Invoke(null, new object[] { p.Name });
p.SetValue(this, value, null);
}
Однако это не очень красиво. На самом деле я задаюсь вопросом, не лучше ли было бы:
static object DoStuff(string name, Type propertyType);
... and then
object value = DoStuff(p.Name, p.PropertyType);
Что дает вам дженерики в этом примере? Обратите внимание, что значения значений будут по-прежнему появляться в коробке и т.д. Во время вызова отражения - и даже тогда бокс не так плох, как вы могли бы подумать.
Наконец, во многих сценариях TypeDescriptor.GetProperties() более подходит, чем Type.GetProperties() - позволяет создавать гибкие объектные модели и т.д.
Ответ 2
Был ли ваш код конструктора предназначен для чтения следующим образом:
public MyClass(){
var properties = this.GetType().GetProperties();
foreach(PropertyInfo p in properties){
p.SetValue(this, DoStuff(p.Name), new object[0]);
}
}
? Обратите внимание на DoStuff
вместо MyClass
.
Если это так, проблема в том, что вы пытаетесь использовать дженерики, когда они действительно не применимы. Точка дженериков (ну, одна из точек) заключается в использовании безопасности типа компиляции. Здесь вы не знаете тип во время компиляции! Вы можете вызвать метод путем отражения (выбор открытой формы, а затем вызов MakeGenericMethod), но это довольно уродливо.
Нужно ли вообще DoStuff
быть родовым? Используется ли он из других источников? Параметр PropertyInfo.SetValue
- это просто объект, поэтому вы все равно получите бокс и т.д., Даже если вы могли бы вызвать метод в целом.
Ответ 3
Если вы не используете DoStuff из другого места, я также предлагаю написать не-общий метод.
Возможно, вы создали общий метод, чтобы иметь возможность использовать значение по умолчанию (T). Чтобы заменить это на не общий метод, вы можете использовать Activator.CreateInstance(T) для типов значений и null для ссылочных типов:
object defaultResult = type.IsValueType ? Activator.CreateInstance(type) : null