2009-06-25 11 views
88

Pracuję nad projektem refleksji, a teraz utknąłem. Jeśli mam obiekt "myclass", który może przechowywać listę , czy ktoś wie, jak uzyskać typ, jak w poniższym kodzie, jeśli właściwość myclass.SomList jest pusta?C# ogólna lista <T> jak uzyskać typ 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; 
      } 
     } 

Odpowiedz

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

Mówiąc bardziej ogólnie, do wspierania wszelkich IList<T>, trzeba sprawdzić interfejsy:

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

nie powinno być typu ItemType = interfaceType.GetGenericArguments() [0]; – Muxa

+0

Zabawnie, drugi przykład kończy się niepowodzeniem, powiedzmy "IList ". Napraw poniżej http://stackoverflow.com/a/13608408/284795 –

+0

Wiem, że odpowiedź jest bardzo stara, ale staram się zrozumieć, co następuje: "GetGenericArguments() [0];" dlaczego jest 0? Czy w Typie jest tylko jedna pozycja? – Ismoh

7

odpowiedź Marca jest podejście używać do tego, ale dla uproszczenia (oraz bardziej przyjazny interfejs API?) można zdefiniować właściwość z klasy bazowej kolekcji, jeśli taką posiadasz:

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

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

Uważam, że to podejście jest użyteczne i łatwe do zrozumienia dla każdego nowo przybyłego na lek generyczny.

+0

Chciałem użyć tego podejścia, ale potem zdałem sobie sprawę, że będę musiał mieć instancję listy, aby określić typ elementu, którego nie zawsze będę miał. – Muxa

+1

możesz uczynić właściwość statyczną.Podobnie jak "public static Type ElementType" ... otrzymujesz go jako "var t = CollectionBase .ElementType;" ... nie potrzebujesz zmiennej instancji – Nabheet

+0

Prawda. Statyczny umożliwia pracę z klasą bez istniejącego obiektu. Uważam, że to podejście jest łatwiejsze w obsłudze. Oczywiście, musisz przechowywać dodatkowe informacje w swoich obiektach, ale przynajmniej oszczędzasz sobie z tego kaskadowego połączenia w zatwierdzonej odpowiedzi. – rbaleksandar

4

Biorąc pod uwagę przedmiot, który podejrzewam być jakiś IList<>, jak mogę określić co To IList<>?

Oto niezawodne rozwiązanie. Przepraszam za długość - API introspekcji C# czyni to zaskakująco trudnym.

/// <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; 
} 

Przykład wykorzystania:

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

Zwraca

True 
typeof(Int32) 
+0

Nie widzę żadnego innego wyniku jako metoda Marcsa (również z marcs otrzymuję Int32) – Offler

17

Biorąc pod uwagę przedmiot, który podejrzewam być jakiś IList<>, jak mogę określić co jest to IList<> ?

Oto odważne rozwiązanie. Zakłada, że ​​masz rzeczywisty obiekt do przetestowania (a nie Type).

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

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

Przykład użycia:

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

Wydruki

typeof(DateTime) 
+0

-1: Dlaczego 'IList ' zamiast 'IEnumerable '? Dlaczego 'dynamic'? –

+0

Czy źle zrozumiałem pytanie OP? Szczegóły były niejasne, więc odpowiedziałem na pytanie w tytule. –

+3

Jesteś sir, jesteś czarodziejem! Stukam w głowę, próbując rozwiązać problem z dynamicznym analizatorem składni o ogólnej strukturze danych (który może zajmować się pojedynczymi typami i listami) i * to * doprowadziło mnie do właściwej ścieżki. –