Определение того, использует ли поле универсальный параметр

#c# #generics #reflection

#c# #общие параметры #отражение

Вопрос:

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

У меня есть класс следующим образом:

 public class Foo<T>
{
    public List<T> Data;
}
  

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

Мой первоначальный подход состоял в том, чтобы продолжать спускаться на как можно больше уровней, и как только я нажму на поле IsGenericParameter, для которого установлено значение true, я бы предпочел не отражать имя типа, а поместить туда строку «Общий аргумент», однако, похоже, я не могу заставить это работать так, как я хочу.

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

Комментарии:

1. Какой у вас есть начальный System.Type экземпляр? Это typeof(Foo<>) или что-то вроде typeof(Foo<SomeType>) ?

2. Привет @dasblinkenlight, экземпляр Foo<SomeType> .

3. Просто чтобы добавить к этому, я затем выполняю итерацию по полям, поэтому при использовании Foo<SomeType> и я добираюсь до поля данных после списка, сама строка не имеет значения ‘IsGenericParameter’ true.

4. Просто чтобы быть уверенным, вы хотите знать, что данные используют универсальный тип?

5. Это IsGenericType то, что вы ищете? например typeof(Foo<int>).GetField("Data").FieldType.IsGenericType

Ответ №1:

Вы хотите IsGenericType , не IsGenericParameter :

 bool isGeneric = typeof(Foo<int>).GetField("Data").FieldType.IsGenericType;
  

Если вы хотите узнать, какой параметр для List является универсальным, тогда вам нужно посмотреть еще на один уровень ниже:

 bool isGeneric = typeof(Foo<>).GetField("Data")
                                 .FieldType
                                 .GetGenericArguments()[0] // only generic argument to List<T>
                                 .IsGenericParameter;
  

что, если Data поле было Dictionary с Dictionary<string, T> . Как бы я определил, какой тип использовал общий параметр?

Вызовите GetGenericArguments тип и просмотрите каждый тип в результирующем массиве

 public class Foo<T>
{
    public Dictionary<string, T> Bar;
}

Type[] types = typeof(Foo<>).GetField("Bar").FieldType.GetGenericArguments();

Console.WriteLine("{0}n{1}",
    types[0].IsGenericParameter,  // false, type is string
    types[1].IsGenericParameter   // true,  type is T
);
  

В основном, IsGenericParameter используется при просмотре общих параметров типа, чтобы увидеть, является ли он универсальным или имеет отдельный тип.

Комментарии:

1. Хорошо, это кажется логичным, но что, если бы поле данных было словарем со словарем<string, T> … Как бы я определил, какой тип использовал общий параметр?

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

Ответ №2:

Вот как отличить универсальные типы, которые полагаются на параметр типа класса, от универсальных типов, которые этого не делают. Рассмотрим этот пример:

 class Foo<T> {
    public List<T> field1;   // You want this field
    public List<int> field2; // not this field
}
  

Начните с получения определения универсального типа и извлечения его аргументов типа:

 var g = typeof(Foo<string>).GetGenericTypeDefinition();
var a = g.GetGenericArguments();
  

Это даст вам массив с единственным типом, который представляет параметр универсального типа T . Теперь вы можете просмотреть все поля и выполнить поиск этого типа среди аргументов универсального типа типов полей, например:

 foreach (var f in g.GetFields()) {
    var ft = f.FieldType;
    if (!ft.IsGenericType) continue;
    var da = ft.GetGenericArguments();
    if (da.Any(xt => a.Contains(xt))) {
        Console.WriteLine("Field {0} uses generic type parameter", f.Name);
    } else {
        Console.WriteLine("Field {0} does not use generic type parameter", f.Name);
    }
}
  

Этот код выдает следующий вывод:

 Field field1 uses generic type parameter
Field field2 does not use generic type parameter