web-dev-qa-db-fra.com

Test si l'objet est de type générique en C #

Je voudrais effectuer un test si un objet est de type générique. J'ai essayé ce qui suit sans succès:

public bool Test()
{
    List<int> list = new List<int>();
    return list.GetType() == typeof(List<>);
}

Qu'est-ce que je fais mal et comment puis-je effectuer ce test?

122
Richbits

Si vous souhaitez vérifier s'il s'agit d'une instance de type générique:

return list.GetType().IsGenericType;

Si vous souhaitez vérifier s'il s'agit d'un générique List<T>:

return list.GetType().GetGenericTypeDefinition() == typeof(List<>);

Comme le souligne Jon, cela vérifie l'équivalence de type exacte. Renvoyer false ne signifie pas nécessairement list is List<T> renvoie false (c'est-à-dire que l'objet ne peut pas être affecté à un List<T> variable).

179
Mehrdad Afshari

Je suppose que vous ne voulez pas seulement savoir si le type est générique, mais si un objet est une instance d'un type générique particulier, sans connaître les arguments de type.

Ce n'est pas terriblement simple, malheureusement. Ce n'est pas trop mal si le type générique est une classe (comme c'est le cas dans ce cas) mais c'est plus difficile pour les interfaces. Voici le code d'une classe:

using System;
using System.Collections.Generic;
using System.Reflection;

class Test
{
    static bool IsInstanceOfGenericType(Type genericType, object instance)
    {
        Type type = instance.GetType();
        while (type != null)
        {
            if (type.IsGenericType &&
                type.GetGenericTypeDefinition() == genericType)
            {
                return true;
            }
            type = type.BaseType;
        }
        return false;
    }

    static void Main(string[] args)
    {
        // True
        Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                  new List<string>()));
        // False
        Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                  new string[0]));
        // True
        Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                  new SubList()));
        // True
        Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                  new SubList<int>()));
    }

    class SubList : List<string>
    {
    }

    class SubList<T> : List<T>
    {
    }
}

EDIT: Comme indiqué dans les commentaires, cela peut fonctionner pour les interfaces:

foreach (var i in type.GetInterfaces())
{
    if (i.IsGenericType && i.GetGenericTypeDefinition() == genericType)
    {
        return true;
    }
}

J'ai une suspicion sournoise, il peut y avoir des cas Edge maladroits autour de cela, mais je ne peux pas en trouver un pour lequel il échoue pour le moment.

81
Jon Skeet

Vous pouvez utiliser un code plus court en utilisant dynamique bien que cela puisse être plus lent que la réflexion pure:

public static class Extension
{
    public static bool IsGenericList(this object o)
    {
       return IsGeneric((dynamic)o);
    }

    public static bool IsGeneric<T>(List<T> o)
    {
       return true;
    }

    public static bool IsGeneric( object o)
    {
        return false;
    }
}



var l = new List<int>();
l.IsGenericList().Should().BeTrue();

var o = new object();
o.IsGenericList().Should().BeFalse();
6
David Desmaisons

Voici mes deux méthodes d'extension préférées qui couvrent la plupart des cas Edge de vérification de type générique:

Marche avec:

  • Interfaces multiples (génériques)
  • Classes de base multiples (génériques)
  • A une surcharge qui "sortira" le type générique spécifique s'il renvoie vrai (voir test unitaire pour les échantillons):

    public static bool IsOfGenericType(this Type typeToCheck, Type genericType)
    {
        Type concreteType;
        return typeToCheck.IsOfGenericType(genericType, out concreteType); 
    }
    
    public static bool IsOfGenericType(this Type typeToCheck, Type genericType, out Type concreteGenericType)
    {
        while (true)
        {
            concreteGenericType = null;
    
            if (genericType == null)
                throw new ArgumentNullException(nameof(genericType));
    
            if (!genericType.IsGenericTypeDefinition)
                throw new ArgumentException("The definition needs to be a GenericTypeDefinition", nameof(genericType));
    
            if (typeToCheck == null || typeToCheck == typeof(object))
                return false;
    
            if (typeToCheck == genericType)
            {
                concreteGenericType = typeToCheck;
                return true;
            }
    
            if ((typeToCheck.IsGenericType ? typeToCheck.GetGenericTypeDefinition() : typeToCheck) == genericType)
            {
                concreteGenericType = typeToCheck;
                return true;
            }
    
            if (genericType.IsInterface)
                foreach (var i in typeToCheck.GetInterfaces())
                    if (i.IsOfGenericType(genericType, out concreteGenericType))
                        return true;
    
            typeToCheck = typeToCheck.BaseType;
        }
    }
    

Voici un test pour démontrer la fonctionnalité (de base):

 [Test]
    public void SimpleGenericInterfaces()
    {
        Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IEnumerable<>)));
        Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IQueryable<>)));

        Type concreteType;
        Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IEnumerable<>), out concreteType));
        Assert.AreEqual(typeof(IEnumerable<string>), concreteType);

        Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IQueryable<>), out concreteType));
        Assert.AreEqual(typeof(IQueryable<string>), concreteType);


    }
5
Wiebe Tijsma
return list.GetType().IsGenericType;
0
Stan R.