2010-12-15 83 views
23

Quel serait le meilleur moyen d'obtenir le type d'éléments qu'une liste générique contient? Il est assez facile de récupérer le premier élément de la collection et d'appeler .GetType(), mais je ne peux pas toujours être sûr qu'il y aura un élément dans la collection.C# - Obtenir le type d'élément pour une liste générique

Espérons que cela a du sens.

Merci,
Sonny

+0

Que savez-vous sur le type déjà? Pourriez-vous fournir un exemple de contexte? –

Répondre

51

Vous pouvez utiliser la méthode Type.GetGenericArguments à cet effet.

List<Foo> myList = ... 

Type myListElementType = myList.GetType().GetGenericArguments().Single(); 
+0

Désolé pour cela étant légèrement hors-sujet, mais comment avez-vous obtenu la coloration de la syntaxe dans votre code? :) – Mehrdad

+1

Si le jeton 'T' est dans la portée (par exemple, dans une méthode qui accepte une' Liste '), vous pouvez aussi utiliser' typeof (T) '. Si la liste est stockée dans une variable de type 'object', vous devrez utiliser l'approche ci-dessus. – cdhowie

+0

N'avez-vous pas répondu à une autre question, presque exactement la même chose, plus tôt aujourd'hui? – LukeH

5
list.GetType().GetGenericArguments()[0] 
+1

voir mon édition. Et voir http://stackoverflow.com/editing-help – jjnguy

+0

Merci! C'est vraiment utile! – Mehrdad

6

Pour une approche plus robuste:

public static Type GetListType(object someList) 
{ 
    if (someList == null) 
     throw new ArgumentNullException("someList"); 

    var type = someList.GetType(); 

    if (!type.IsGenericType || type.GetGenericTypeDefinition() != typeof(List<>)) 
     throw new ArgumentException("someList", "Type must be List<>, but was " + type.FullName); 

    return type.GetGenericArguments()[0]; 
} 

Mais si votre variable est typée List<T> alors vous pouvez simplement utiliser typeof(T). Par exemple:

public static Type GetListType<T>(List<T> someList) 
{ 
    return typeof(T); 
} 

Notez que vous ne même pas vraiment besoin du paramètre someList. Cette méthode est juste un exemple de comment vous pouvez utiliser typeof si vous êtes déjà dans une méthode générique. Vous n'avez besoin d'utiliser la méthode de réflexion que si vous n'avez pas accès au jeton T (la liste est stockée dans une variable non générique, par exemple IList, object, etc.).

+0

Vache sacrée, bonne gestion des erreurs. Vous pouvez également ajouter un texte d'erreur localisable. ;) – Mehrdad

+0

@Lambert: Je vais laisser cela comme un exercice pour le lecteur. :) – cdhowie

0
Public Shared Function ListItemType(ListType As System.Type) As System.Type 

    If Not ListType.IsGenericType Then 
    If ListType.BaseType IsNot Nothing AndAlso ListType.BaseType.IsGenericType Then 
     Return ListItemType(ListType.BaseType) 
    End If 
    Else 
    Return ListType.GetGenericArguments.Single 
    End If 
End Function 
1

Qu'en est-ce, tous ses statiques (par exemple, aucun cas nécessaire) et rapide (pas de boucle, aucune utilisation de LINQ), et il est simple :) ces travaux pour les collections:

[System.Diagnostics.DebuggerHidden] 
    public static Type GetIndexedType(this ICollection poICollection) 
    { 
     PropertyInfo oPropertyInfo = poICollection == null ? null : poICollection.GetType().GetProperty("Item"); 
     return oPropertyInfo == null ? null : oPropertyInfo.PropertyType; 
    } 

    [System.Diagnostics.DebuggerHidden] 
    public static Type GetEnumeratedType(this ICollection poICollection) 
    { 
     PropertyInfo oPropertyInfo = poICollection == null ? null : poICollection.GetType().GetMethod("GetEnumerator").ReturnType.GetProperty("Current"); 
     return oPropertyInfo == null ? null : oPropertyInfo.PropertyType; 
    } 

et quelques tests simples unitaires:

 [Test] 
     public void GetIndexedType() 
     { 
      Assert.AreEqual(null, ((ICollection)null).GetIndexedType()); 
      Assert.AreEqual(typeof(int), (new List<int>()).GetIndexedType()); 
      Assert.AreEqual(typeof(bool), (new SortedList<string, bool>()).GetIndexedType()); 
     } 

     [Test] 
     public void GetEnumeratedType() 
     { 
      Assert.AreEqual(null, ((ICollection)null).GetEnumeratedType()); 
      Assert.AreEqual(typeof(int), (new List<int>()).GetEnumeratedType()); 
      Assert.AreEqual(typeof(KeyValuePair<string, bool>), (new SortedList<string, bool>()).GetEnumeratedType()); 
     } 

Remarque le fait qu'il ya deux façons de voir cela, un type peut être retourné par l'indexeur et un autre type peuvent être retournés par le recenseur. Le test unitaire montre les deux.

Amusez-vous, Frans.

P.s. Pour enumerables:

[System.Diagnostics.DebuggerHidden] 
    public static Type GetEnumeratedType(this System.Collections.IEnumerable poIEnumerable) 
    { 
     PropertyInfo oPropertyInfo = poIEnumerable == null ? null : poIEnumerable.GetType().GetMethod("GetEnumerator").ReturnType.GetProperty("Current"); 
     return oPropertyInfo == null ? null : oPropertyInfo.PropertyType; 
    } 

Et pour recenseur:

[System.Diagnostics.DebuggerHidden] 
    public static Type GetEnumeratedType(this System.Collections.IEnumerator poIEnumerator) 
    { 
     PropertyInfo oPropertyInfo = poIEnumerator == null ? null : poIEnumerator.GetType().GetProperty("Current"); 
     return oPropertyInfo == null ? null : oPropertyInfo.PropertyType; 
    } 
+0

Vos méthodes acceptent ici les versions non génériques de chaque interface, mais ne fonctionneront que si elles implémentent les versions génériques. Si vous avez ces exigences, vous pouvez tout aussi bien avoir les méthodes acceptant directement les versions génériques des interfaces. – Servy

+0

Omg, cela fonctionne. –

4

Voici une autre manière qui fonctionne pour les collections non génériques aussi:

static Type GetItemType(Type collectionType) 
{ 
    return collectionType.GetMethod("get_Item").ReturnType; 
} 

C'est, obtenir le type de retour foo[x] , où foo est du type spécifié.

Exemples:

// Generic type; prints System.Int32 
Console.WriteLine(GetItemType(typeof(List<int>))); 

// Non-generic type; prints System.String 
Console.WriteLine(GetItemType(typeof(System.Collections.Specialized.StringCollection))); 

La méthode GetItemType ci-dessus a un problèmes de couple, bien que:

  • Il jette un NullReferenceException si le type n'a pas d'opérateur d'indexation.

  • Il déclenche une AmbiguousMatchException si le type a de multiples surcharges pour l'opérateur d'indexation (par exemple this[string] et this[int]).

Voici une version plus raffinée:

public static Type GetItemType(this Type collectionType) 
{ 
    var types = 
     (from method in collectionType.GetMethods() 
     where method.Name == "get_Item" 
     select method.ReturnType 
     ).Distinct().ToArray(); 
    if (types.Length == 0) 
     return null; 
    if (types.Length != 1) 
     throw new Exception(string.Format("{0} has multiple item types", collectionType.FullName)); 
    return types[0]; 
} 
0

vieille question nouvelle méthode avec dynamic

void Foo(){ 
    Type type GetTypeT(data as dynamic); 
} 

private static Type GetTypeT<T>(IEnumerable<T> data) 
{ 
    return typeof(T); 
} 
0
public Type GetType(IEnumerable<object> resultList) 
    { 
     return resultList.GetType().GetElementType(); 
    } 
+0

Bien que cet extrait de code puisse résoudre la question, [y compris une explication] (// meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) contribue vraiment à améliorer la qualité de votre message. Rappelez-vous que vous répondez à la question pour les lecteurs dans le futur, et que ces personnes pourraient ne pas connaître les raisons de votre suggestion de code. Essayez également de ne pas surcharger votre code avec des commentaires explicatifs, car cela réduit la lisibilité du code et des explications! – FrankerZ