web-dev-qa-db-fra.com

Comment obtenir le type de T d'un membre d'une classe ou d'une méthode générique?

Disons que j'ai un membre générique dans une classe ou une méthode, donc:

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

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

Lorsque j'instancie la classe, la T devient MyTypeObject1 et la classe a donc une propriété de liste générique: List<MyTypeObject1>. La même chose s'applique à 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é list appelée Bar ou la variable locale baz contient quel type de T

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

593
Patrick Desjardins

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

Type typeParameterType = typeof(T);

Si vous avez la chance d'avoir object comme paramètre de type, voir Réponse de Marc .

594
Tamas Czinege

(Remarque: je suppose que tout ce que vous savez est object ou IList ou similaire, et que la liste pourrait être de tout type à l'exécution)

Si vous savez que c'est un List<T>, alors:

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

Une autre option est de regarder l'indexeur:

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

Utilisation du nouveau TypeInfo:

using System.Reflection;
// ...
var type = abc.GetType().GetTypeInfo().GenericTypeArguments[0];
484
Marc Gravell

Avec la méthode d'extension suivante, vous pouvez vous échapper sans réfléchir:

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

Usage:

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
43
3dGrabber

Essayer

list.GetType().GetGenericArguments()
29
Rauhotz

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

IEnumerable myEnum = myList as IEnumerable;
Type entryType = myEnum.AsQueryable().ElementType;
13
Carlos Rodriguez

Considérez ceci:

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);
    }
}
9
Ferenc Mucsi

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

T checkType = default(T);

if (checkType is MyClass)
{}
9
Sebi

Vous pouvez utiliser celui-ci pour le type de retour de liste générique:

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

La méthode GetGenericArgument() doit être définie sur le type de base de votre instance (Dont la classe est une classe générique myClass<T>). Sinon, il retourne un type [0]

Exemple:

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

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

public static string StripStartingWith(this string s, string stripAfter)
{
    if (s == null)
    {
        return null;
    }
    var indexOf = s.IndexOf(stripAfter, StringComparison.Ordinal);
    if (indexOf > -1)
    {
        return s.Substring(0, indexOf);
    }
    return s;
}

Vous l'utilisez 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 recherchez, mais cela aide à démontrer les techniques impliquées. 

4
Ken Smith

Vous pouvez obtenir le type de "T" à partir de n'importe quel type de collection qui implémente IEnumerable <T> avec les éléments suivants:

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 prend pas en charge les types de collection qui implémentent 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 aviez besoin de supporter ceci ...

En outre, vous pouvez également souhaiter mettre le résultat en cache par type de collection si vous le faites souvent (par exemple, en boucle).

4
Dan Malcolm

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();
1
fantastory
public bool IsCollection<T>(T value){
  var valueType = value.GetType();
  return valueType.IsArray() || typeof(IEnumerable<object>).IsAssignableFrom(valueType) || typeof(IEnumerable<T>).IsAssignableFrom(valuetype);
}
1
Karanvir Kang

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

Alors appelle ça comme ça 

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

OR

var item = Activator.CreateInstance(Bar.GetType().GetElementType());
0
Alen.Toma

Si vous voulez connaître le type sous-jacent d'une propriété, essayez ceci:

propInfo.PropertyType.UnderlyingSystemType.GenericTypeArguments[0]
0
Fatih Çelik

Pour les développeurs qui cherchent à traiter littéralement avec des noms de type, par exemple pour utiliser une instruction switch, voici un exemple:

    public static void TypeBasedLogic<T>(T item)
    {
        switch (typeof(T).ToString()) //e.g. System.Int32
        {
            case nameof(System) + "." + nameof(Int32):
                //Logic for int
                Console.Write("It's an integer");
            break;

            case nameof(System) + "." + nameof(Double):
                //Logic for double
                Console.Write("It's a double");
            break;

            case nameof(System) + "." + nameof(Decimal):
                //Logic for decimal
                Console.Write("It's a decimal");
            break;

            case nameof(System) + "." + nameof(String):
                //Logic for string
                Console.Write("It's a string");
            break;

            default:
                //logic for the rest other System or custom types
                Console.Write("It's a "+ typeof(T).ToString());
            break;

        }
    }

usage:

TypeBasedLogic(5); // outputs: It's an integer
0
Shadi Namrouti