Отражение и универсальные типы - PullRequest
12 голосов
/ 13 октября 2008

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

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

Так, как мне передать тип собственности в?

Ответы [ 3 ]

19 голосов
/ 13 октября 2008

Вы хотите вызвать DoStuff с T = типом каждого свойства? В этом случае, «как есть», вам нужно будет использовать рефлексию и MakeGenericMethod - т.е.

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 () - допускает гибкие объектные модели и т. Д.

7 голосов
/ 13 октября 2008

Код вашего конструктора должен был выглядеть так:

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 - это просто объект, поэтому вы все равно получите бокс и т. Д., Даже если вы сможете вызвать метод в общем.

2 голосов
/ 13 октября 2008

Если вы не используете DoStuff из другого места, я также предлагаю написать неуниверсальный метод.

Возможно, вы создали универсальный метод, чтобы иметь возможность использовать default (T). Чтобы заменить это в неуниверсальном методе, вы можете использовать Activator.CreateInstance (T) для типов значений и нуль для ссылочных типов:

object defaultResult = type.IsValueType ? Activator.CreateInstance(type) : null
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...