2009-02-17 6 views
515

Disons que j'ai un membre générique dans une classe ou une méthode, donc:Comment obtenir le type de T d'un membre d'une classe ou méthode générique?

public class Foo<T> 
{ 
    public List<T> Bar { get; set; } 

    public void Baz() 
    { 
     // get type of T 
    } 
} 

Quand j'instancier la classe, le T devient MyTypeObject1, de sorte que la classe a une propriété de liste générique: List<MyTypeObject1>. De même pour une méthode générique dans une classe non générique:

public class Foo 
{ 
    public void Bar<T>() 
    { 
     var baz = new List<T>(); 

     // get type of T 
    } 
} 

Je voudrais savoir, quel type d'objets la liste de ma classe contient. Ainsi, la propriété de liste appelée Bar ou la variable locale baz, contient quel type de T?

Je ne peux pas faire Bar[0].GetType(), car la liste peut contenir zéro élément. Comment puis-je le faire?

Répondre

514

Si je comprends bien, votre liste a le même paramètre de type que la classe de conteneur lui-même. Si tel est le cas, alors:

Type typeParameterType = typeof(T); 

Si vous êtes dans la situation de la chance d'avoir object comme paramètre de type, voir Marc's answer.

+4

Lol - oui, très vrai; J'ai supposé que l'OP avait seulement 'object',' IList', ou similaire - mais cela pourrait très bien être la bonne réponse. –

+13

J'aime la lisibilité de 'typeof'. Si vous voulez connaître le type de T, il suffit d'utiliser 'typeof (T)' :) – demoncodemonkey

+1

En fait, je viens d'utiliser typeof (Type) et cela fonctionne très bien. – Anton

442

(note: Je suppose que tout ce que vous connaissez est object ou IList ou similaire, et que la liste pourrait être tout type à l'exécution)

Si vous le savez est un List<T>, puis:

Type type = abc.GetType().GetGenericArguments()[0]; 

Une autre option est de regarder l'indexeur:

Type type = abc.GetType().GetProperty("Item").PropertyType; 

nouvelle aide TypeInfo:

using System.Reflection; 
// ... 
var type = abc.GetType().GetTypeInfo().GenericTypeArguments[0]; 
+1

GetGenericArguments Type type = abc.GetType().() [ 0]; ==> Index de tableau hors limites ... –

+22

@Daok: alors ce n'est pas une liste

+0

Besoin de quelque chose pour BindingList ou List ou n'importe quel objet qui détiennent un . Ce que je fais utilise un BindingListView personnalisé

26

Essayez

list.GetType().GetGenericArguments() 
+0

Cela ne fonctionne pas .... GetGenericArguments return System.Type [0] –

+5

nouvelle Liste () .GetType(). GetGenericArguments() renvoie System.Type [1] ici avec System.Int32 comme entrée – Rauhotz

-9

Type:

type = list.AsEnumerable().SingleOrDefault().GetType(); 
+1

Cela lancerait une exception NullReferenceException si la liste ne contenait aucun élément à tester. – rossisdead

+1

'SingleOrDefault()' lance également 'InvalidOperationException' lorsqu'il y a deux éléments ou plus. – devgeezer

+0

Cette réponse est fausse, comme l'ont clairement indiqué \ @rossisdead et \ @devgeezer. – Oliver

9

Considérez ceci: Je l'utilise pour exporter 20 liste dactylographiée par même:

private void Generate<T>() 
{ 
    T item = (T)Activator.CreateInstance(typeof(T)); 

    ((T)item as DemomigrItemList).Initialize(); 

    Type type = ((T)item as DemomigrItemList).AsEnumerable().FirstOrDefault().GetType(); 
    if (type == null) return; 
    if (type != typeof(account)) //account is listitem in List<account> 
    { 
     ((T)item as DemomigrItemList).CreateCSV(type); 
    } 
} 
+1

Cela ne fonctionne pas si T est une super-classe abstraite des objets ajoutés réels. Pour ne pas mentionner, juste 'new T();' ferait la même chose que '(T) Activator.CreateInstance (typeof (T));'. Cela nécessite que vous ajoutiez 'where T: new()' à la définition de classe/fonction, mais si vous voulez _want_ faire des objets, cela devrait être fait de toute façon. – Nyerguds

+0

En outre, vous appelez 'GetType' sur une entrée' FirstOrDefault' résultant en une exception de référence null potentiel. Si vous êtes sûr qu'il retournera au moins un élément, pourquoi ne pas utiliser 'First' à la place? –

5

La méthode GetGenericArgument() doit être mis sur le type de base de votre instance (dont la classe est une classe générique myClass<T>). Dans le cas contraire, elle renvoie un type [0]

Exemple:

Myclass<T> instance = new Myclass<T>(); 
Type[] listTypes = typeof(instance).BaseType.GetGenericArguments(); 
12

C'est un travail pour moi. Où myList est une sorte de liste inconnue.

IEnumerable myEnum = myList as IEnumerable; 
Type entryType = myEnum.AsQueryable().ElementType; 
+1

Je reçois une erreur qui nécessite un argument de type (c'est-à-dire '') –

+0

Joseph et autres, pour se débarrasser de l'erreur, il est dans System.Collections. – user2334883

+1

Juste la deuxième ligne est nécessaire pour moi. Une 'List' est déjà une implémentation de' IEnumerable', donc la distribution ne semble rien ajouter. Mais merci, c'est une bonne solution. – pipedreambomb

41

Avec la méthode d'extension suivante, vous pouvez partir sans réflexion:

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

Ou plus général:

public static Type GetEnumeratedType<T>(this IEnumerable<T> _) 
{ 
    return typeof(T); 
} 

Utilisation:

List<string>  list = new List<string> { "a", "b", "c" }; 
IEnumerable<string> strings = list; 
IEnumerable<object> objects = list; 

Type listType = list.GetListType();   // string 
Type stringsType = strings.GetEnumeratedType(); // string 
Type objectsType = objects.GetEnumeratedType(); // BEWARE: object 
+5

Cela n'est utile que si vous connaissez déjà le type de 'T' au moment de la compilation. Dans ce cas, vous n'avez vraiment pas besoin de code du tout. – recursive

+0

'return default (T);' – fantastory

+1

@recursive: C'est utile si vous travaillez avec une liste d'un type anonyme. – JJJ

3

Vous pouvez obtenir le type de "T" de n'importe quel type de collection qui implémente IEnumerable <T> ce qui suit:

public static Type GetCollectionItemType(Type collectionType) 
{ 
    var types = collectionType.GetInterfaces() 
     .Where(x => x.IsGenericType 
      && x.GetGenericTypeDefinition() == typeof(IEnumerable<>)) 
     .ToArray(); 
    // Only support collections that implement IEnumerable<T> once. 
    return types.Length == 1 ? types[0].GetGenericArguments()[0] : null; 
} 

Notez qu'il ne supporte pas les types de collections qui mettent en œuvre IEnumerable <T> deux fois, par exemple

public class WierdCustomType : IEnumerable<int>, IEnumerable<string> { ... } 

Je suppose que vous pourriez retourner un tableau de types si vous avez besoin pour soutenir cette ...

, vous pouvez également également mettre en cache le résultat par type de collection si vous faites cela beaucoup (par exemple dans une boucle).

2

J'utilise cette méthode d'extension pour accomplir quelque chose de similaire:

public static string GetFriendlyTypeName(this Type t) 
{ 
    var typeName = t.Name.StripStartingWith("`"); 
    var genericArgs = t.GetGenericArguments(); 
    if (genericArgs.Length > 0) 
    { 
     typeName += "<"; 
     foreach (var genericArg in genericArgs) 
     { 
      typeName += genericArg.GetFriendlyTypeName() + ", "; 
     } 
     typeName = typeName.TrimEnd(',', ' ') + ">"; 
    } 
    return typeName; 
} 

Vous pouvez l'utiliser comme ceci:

[TestMethod] 
public void GetFriendlyTypeName_ShouldHandleReallyComplexTypes() 
{ 
    typeof(Dictionary<string, Dictionary<string, object>>).GetFriendlyTypeName() 
     .ShouldEqual("Dictionary<String, Dictionary<String, Object>>"); 
} 

Ce n'est pas tout à fait ce que vous cherchez, mais il est utile dans démontrer les techniques impliquées.

0
public bool IsCollection<T>(T value){ 
    var valueType = value.GetType(); 
    return valueType.IsArray() || typeof(IEnumerable<object>).IsAssignableFrom(valueType) || typeof(IEnumerable<T>).IsAssignableFrom(valuetype); 
} 
+0

Cela semble répondre à la question de savoir si le type est une sorte de liste-y, mais la question est plus sur la façon de déterminer paramètre de type générique un type connu comme étant une liste déjà initialisée avec. –

4

Si vous n'avez pas besoin de la variable Type entière et que vous voulez simplement vérifier le type, vous pouvez facilement créer une variable temp et utiliser l'opérateur.

T checkType = default(T); 

if (checkType is MyClass) 
{} 
1

En utilisant la solution de 3dGrabber:

public static T GetEnumeratedType<T>(this IEnumerable<T> _) 
{ 
    return default(T); 
} 

//and now 

var list = new Dictionary<string, int>(); 
var stronglyTypedVar = list.GetEnumeratedType(); 
0

Si vous voulez savoir type sous-jacent d'une propriété, essayez ceci:

propInfo.PropertyType.UnderlyingSystemType.GenericTypeArguments[0] 
3
public string ListType<T>(T value){ 
    var valueType = value.GetType().GenericTypeArguments[0].FullName; 
    return value; 
} 

Vous pouvez utiliser celui-ci pour le type réexécutez de liste générique.

0

Voici comment je l'ai fait

internal static Type GetElementType(this Type type) 
{ 
     //use type.GenericTypeArguments if exist 
     if (type.GenericTypeArguments.Any()) 
     return type.GenericTypeArguments.First(); 

     return type.GetRuntimeProperty("Item").PropertyType); 
} 

Puis appelle comme ça

var item = Activator.CreateInstance(iListType.GetElementType()); 

OU

var item = Activator.CreateInstance(Bar.GetType().GetElementType()); 
Questions connexes