2009-06-25 9 views
88

Sto lavorando a un progetto di riflessione e ora sono bloccato. Se ho un oggetto "myclass" che può contenere una lista qualcuno sa come ottenere il tipo come nel codice qui sotto se la proprietà myclass.SomList è vuota?C# elenco generico <T> come ottenere il tipo di 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; 
      } 
     } 

risposta

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

Più in generale, per supportare qualsiasi IList<T>, è necessario controllare le interfacce:

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

non dovrebbe essere di tipo itemtype = interfaceType.GetGenericArguments() [0]; – Muxa

+0

In modo divertente, il secondo esempio fallisce con, diciamo 'IList '. Correzione di seguito http://stackoverflow.com/a/13608408/284795 –

+0

So che la risposta è molto vecchia, ma cerco di capire quanto segue: 'GetGenericArguments() [0];' perché c'è lo 0? C'è solo un elemento nel Tipo []? – Ismoh

7

risposta di Marc è l'approccio che uso per questo, ma per semplicità (e un'API più amichevole?) puoi definire una proprietà nella classe base di raccolta se ne hai una come:

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

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

Ho trovato questo approccio utile ed è facile da capire per tutti i neofiti dei generici.

+0

Volevo utilizzare questo approccio, ma poi ho capito che dovrò avere l'istanza di lista per determinare il tipo di elemento, che non sempre avrò. – Muxa

+1

è possibile rendere statica la proprietà.Come "public static Type ElementType" ... quindi ci si arriva come "var t = CollectionBase .ElementType;" ... non avrai bisogno di una variabile di istanza – Nabheet

+0

True. Statico ti consente di lavorare con una classe senza un oggetto esistente. Trovo questo approccio più facile da lavorare. Certo, è necessario memorizzare ulteriori informazioni nei tuoi oggetti ma almeno ti risparmi da questa chiamata a cascata nella risposta approvata. – rbaleksandar

4

Dato un oggetto che ho il sospetto di essere una sorta di IList<>, come posso determinare di ciò è un IList<>?

Ecco una soluzione affidabile. Le mie scuse per la lunghezza - l'API di introspezione di C# rende questo sorprendentemente difficile.

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

esempio di utilizzo:

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

Returns

True 
typeof(Int32) 
+0

Non vedo nessun altro risultato come metodo Marcs (anche con marcs ottengo Int32) – Offler

17

Dato un oggetto che ho il sospetto di essere una sorta di IList<>, come posso determinare di ciò è un IList<> ?

Ecco la soluzione coraggiosa. Si presuppone che tu abbia l'oggetto reale da testare (piuttosto che uno Type). Utilizzo

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

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

Esempio:

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

stampe

typeof(DateTime) 
+0

-1: Perché 'IList ' invece di 'IEnumerable '? Perché 'dinamico'? –

+0

Ho frainteso la domanda di OP? Il dettaglio non era chiaro, quindi ho risposto alla domanda nel titolo. –

+3

Signore, sei un mago! Ho sbattuto la testa cercando di risolvere un problema con un parser dinamico con una struttura dati generica (che può gestire tipi e elenchi singolari) e * questo * mi ha portato sulla strada giusta. –