Отражение и общие типы

Я пишу код для конструктора классов, который перебирает все свойства класса и вызывает общий статический метод, который заполняет мой класс данными из внешнего 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