2009-06-25 2 views
88

Я работаю над проектом отражения, и теперь я застрял. Если у меня есть объект «myclass», который может содержать список , то кто-нибудь знает, как получить тип, как в приведенном ниже коде, если свойство myclass.SomList пуст?C# общий список <T> как получить тип T?

List<myclass> myList = dataGenerator.getMyClasses(); 
lbxObjects.ItemsSource = myList; 
lbxObjects.SelectionChanged += lbxObjects_SelectionChanged; 

private void lbxObjects_SelectionChanged(object sender, SelectionChangedEventArgs e) 
     { 
      Reflect(); 
     } 
Private void Reflect() 
{ 
foreach (PropertyInfo pi in lbxObjects.SelectedItem.GetType().GetProperties()) 
{ 
     switch (pi.PropertyType.Name.ToLower()) 
     { 
     case "list`1": 
      {   
      // This works if the List<T> contains one or more elements. 
      Type tTemp = GetGenericType(pi.GetValue(lbxObjects.SelectedItem, null)); 

      // but how is it possible to get the Type if the value is null? 
      // I need to be able to create a new object of the type the generic list expect. 
      // Type type = pi.getType?? // how to get the Type of the class inside List<T>? 
      break; 
      } 
     } 
} 
} 
private Type GetGenericType(object obj) 
     { 
      if (obj != null) 
      { 
       Type t = obj.GetType(); 
       if (t.IsGenericType) 
       { 
        Type[] at = t.GetGenericArguments(); 
        t = at.First<Type>(); 
       } return t; 
      } 
      else 
      { 
       return null; 
      } 
     } 

ответ

174
Type type = pi.PropertyType.PropertyType; 
if(type.IsGenericType && type.GetGenericTypeDefinition() 
     == typeof(List<>)) 
{ 
    Type itemType = type.GetGenericArguments()[0]; // use this... 
} 

В целом, для поддержки любого IList<T>, вам необходимо проверить интерфейсы:

foreach (Type interfaceType in type.GetInterfaces()) 
{ 
    if (interfaceType.IsGenericType && 
     interfaceType.GetGenericTypeDefinition() 
     == typeof(IList<>)) 
    { 
     Type itemType = type.GetGenericArguments()[0]; 
     // do something... 
     break; 
    } 
} 
+8

не должна быть типа ItemType = interfaceType.GetGenericArguments() [0]; – Muxa

+0

Смешно, второй пример не выполняется, скажем 'IList '. Исправление ниже http://stackoverflow.com/a/13608408/284795 –

+0

Я знаю, что ответ очень старый, но я стараюсь понять следующее: 'GetGenericArguments() [0];' почему существует 0? Есть ли только один элемент в типе []? – Ismoh

7

ответ Marc является подход я использую для этого, но для простоты (и более дружественный API?), вы можете определить свойство в базовом классе коллекции, если у вас есть один из них:

public abstract class CollectionBase<T> : IList<T> 
{ 
    ... 

    public Type ElementType 
    { 
     get 
     { 
     return typeof(T); 
     } 
    } 
} 

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

+0

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

+1

вы можете сделать свойство статическим.Подобно «public static Type ElementType» ..., вы получаете на нем «var t = CollectionBase .ElementType;» ... вам не понадобится переменная экземпляра – Nabheet

+0

True. Static позволяет работать с классом без существующего объекта. Я считаю этот подход более удобным для работы. Конечно, вам нужно сохранить дополнительную информацию в своих объектах, но по крайней мере вы избавитесь от этого каскадного вызова в одобренном ответе. – rbaleksandar

4

Учитывая объект, который я подозреваю, что какой-то IList<>, как я могу определить, что это в IList<>?

Это надежное решение. Мои извинения за длину - API-интерфейс интроспекции C# делает это чрезвычайно сложным.

/// <summary> 
/// Test if a type implements IList of T, and if so, determine T. 
/// </summary> 
public static bool TryListOfWhat(Type type, out Type innerType) 
{ 
    Contract.Requires(type != null); 

    var interfaceTest = new Func<Type, Type>(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IList<>) ? i.GetGenericArguments().Single() : null); 

    innerType = interfaceTest(type); 
    if (innerType != null) 
    { 
     return true; 
    } 

    foreach (var i in type.GetInterfaces()) 
    { 
     innerType = interfaceTest(i); 
     if (innerType != null) 
     { 
      return true; 
     } 
    } 

    return false; 
} 

Пример использования:

object value = new ObservableCollection<int>(); 
Type innerType; 
TryListOfWhat(value.GetType(), out innerType).Dump(); 
innerType.Dump(); 

Возвращает

True 
typeof(Int32) 
+0

Я не вижу никакого другого результата в качестве метода Marcs (также с марками i get Int32) – Offler

17

Учитывая объект, который я подозреваю, что какой-то IList<>, как я могу определить из какой что это IList<> ?

Вот решение gutsy. Предполагается, что у вас есть фактический объект для тестирования (а не Type). Использование

public static Type ListOfWhat(Object list) 
{ 
    return ListOfWhat2((dynamic)list); 
} 

private static Type ListOfWhat2<T>(IList<T> list) 
{ 
    return typeof(T); 
} 

Пример:

object value = new ObservableCollection<DateTime>(); 
ListOfWhat(value).Dump(); 

Отпечатки

typeof(DateTime) 
+0

-1: Почему '' IList 'вместо' IEnumerable '? Почему «динамический»? –

+0

Я неправильно понял вопрос ОФ? Детали были неясны, поэтому я ответил на вопрос в названии. –

+3

Вы, сэр, волшебник! Я ударился головой, пытаясь решить проблему с динамическим парсером с общей структурой данных (которая может иметь дело с особыми типами и списками), и * это * заставило меня на правильном пути. –