332 votes

Vérifiez si une classe est dérivée d'une classe générique

J'ai une classe générique dans mon projet avec des classes dérivées.

public class GenericClass : GenericInterface
{
}

public class Test : GenericClass
{
}

Existe-t-il un moyen de savoir si un objet de type Type est dérivé de GenericClass ?

t.IsSubclassOf(typeof(GenericClass<>))

ne fonctionne pas.

10voto

Voici une petite méthode que j'ai créée pour vérifier qu'un objet est dérivé d'un type spécifique. Ça fonctionne très bien pour moi!

internal static bool IsDerivativeOf(this Type t, Type typeToCompare)
{
    if (t == null) throw new NullReferenceException();
    if (t.BaseType == null) return false;

    if (t.BaseType == typeToCompare) return true;
    else return t.BaseType.IsDerivativeOf(typeToCompare);
}

9voto

DVK Points 116

Cela peut être fait facilement avec Linq. Cela trouvera tout type qui est une sous-classe de la classe de base générique GenericBaseType.

    IEnumerable allTypes = Assembly.GetExecutingAssembly().GetTypes();

    IEnumerable mySubclasses = allTypes.Where(t => t.BaseType != null 
                                                            && t.BaseType.IsGenericType
                                                            && t.BaseType.GetGenericTypeDefinition() == typeof(GenericBaseType<,>));

8voto

kad81 Points 782

Solution simple : il suffit de créer et d'ajouter une deuxième interface non générique à la classe générique :

public interface IGenericClass
{
}

public class GenericClass : GenericInterface, IGenericClass
{
}

Ensuite, il suffit de vérifier de la manière qui vous convient en utilisant is, as, IsAssignableFrom, etc.

if (chose is IGenericClass)
{
    // Faire fonctionner
{

Évidemment, cela n'est possible que si vous avez la possibilité de modifier la classe générique (comme semble le faire l'OP), mais c'est un peu plus élégant et lisible que d'utiliser une méthode d'extension cryptique.

7voto

it depends Points 2361

Réponse mise à jour

Cette méthode vérifie si typeA est égal, hérite (classe : classe), implémente (classe : interface) ou étend (interface : interface) typeB. Elle accepte des interfaces et des classes génériques et non génériques.

public static bool Satisfies(Type typeA, Type typeB)
{
    var types = new List(typeA.GetInterfaces());
    for (var t = typeA; t != null; t = t.BaseType)
    {
        types.Add(t);
    }
    return types.Any(t =>
        t == typeB ||
            t.IsGenericType && (t.GetGenericTypeDefinition() == typeB));
}

Utilisée dans ce qui suit, elle réussit 74 des 76 tests de la réponse de @Xav987 (elle ne réussit pas les tests '68-3' et '69-3 mais je pense que ces tests impliquent que List est une sous-classe de List et je ne crois pas que ce soit le cas. Par exemple, List ne peut pas être convertie en List, voir https://stackoverflow.com/a/9891849/53252.)

public static bool IsSubClassOfGeneric(this Type typeA, Type typeB)
{
    if (typeA == typeB)
    {
        return false;
    }
    return Satisfies(typeA, typeB);
}

Exemples :

using System.Collections;
using System.Numerics;

void ShowSatisfaction(Type typeA, Type typeB)
{
    var satisfied = Satisfies(typeA, typeB);
    Console.WriteLine($"{satisfied}: [{typeA}]  satisfait  [{typeB}]");
}

ShowSatisfaction(typeof(object), typeof(string));
ShowSatisfaction(typeof(string), typeof(object));
ShowSatisfaction(typeof(string), typeof(IEnumerable));
ShowSatisfaction(typeof(string), typeof(IEnumerable<>));
ShowSatisfaction(typeof(string), typeof(IEnumerable));
ShowSatisfaction(typeof(string), typeof(IEnumerable));
ShowSatisfaction(typeof(int), typeof(object));
ShowSatisfaction(typeof(int), typeof(IComparable));
ShowSatisfaction(typeof(IReadOnlyDictionary<,>), typeof(IReadOnlyCollection<>));
ShowSatisfaction(typeof(bool), typeof(INumber<>));
ShowSatisfaction(typeof(int), typeof(INumber<>));
ShowSatisfaction(typeof(IBinaryInteger<>), typeof(IShiftOperators<,>));
ShowSatisfaction(typeof(IBinaryInteger), typeof(IShiftOperators<,>));
ShowSatisfaction(typeof(IBinaryInteger), typeof(IShiftOperators));

Sortie :

False: [System.Object]  satisfait  [System.String]
True: [System.String]  satisfait  [System.Object]
True: [System.String]  satisfait  [System.Collections.IEnumerable]
True: [System.String]  satisfait  [System.Collections.Generic.IEnumerable`1[T]]
True: [System.String]  satisfait  [System.Collections.Generic.IEnumerable`1[System.Char]]
False: [System.String]  satisfait  [System.Collections.Generic.IEnumerable`1[System.Int32]]
True: [System.Int32]  satisfait  [System.Object]
True: [System.Int32]  satisfait  [System.IComparable]
True: [System.Collections.Generic.IReadOnlyDictionary`2[TKey,TValue]]  satisfait  [System.Collections.Generic.IReadOnlyCollection`1[T]]
False: [System.Boolean]  satisfait  [System.Numerics.INumber`1[TSelf]]
True: [System.Int32]  satisfait  [System.Numerics.INumber`1[TSelf]]
True: [System.Numerics.IBinaryInteger`1[TSelf]]  satisfait  [System.Numerics.IShiftOperators`2[TSelf,TResult]]
True: [System.Numerics.IBinaryInteger`1[System.Int32]]  satisfait  [System.Numerics.IShiftOperators`2[TSelf,TResult]]
True: [System.Numerics.IBinaryInteger`1[System.Int32]]  satisfait  [System.Numerics.IShiftOperators`2[System.Int32,System.Int32]]

Les exemples INumber<> proviennent de .NET 7 en prévisualisation 5.

Ancienne réponse

Cela peut sembler excessif, mais j'utilise des méthodes d'extension comme celles-ci. Elles vérifient les interfaces ainsi que les sous-classes. Elles peuvent également renvoyer le type qui a la définition générique spécifiée.

Par exemple, pour l'exemple dans la question, cela peut tester une interface générique ainsi qu'une classe générique. Le type retourné peut être utilisé avec GetGenericArguments pour déterminer que le type d'argument générique est "SomeType".

/// 
/// Vérifie si ce type a la définition spécifiée dans son ascendance.
///    
public static bool HasGenericDefinition(this Type type, Type definition)
{
    return GetTypeWithGenericDefinition(type, definition) != null;
}

/// 
/// Renvoie le type réel implémentant la définition spécifiée dans
/// l'ascendance du type, si disponible. Sinon, null.
/// 
public static Type GetTypeWithGenericDefinition(this Type type, Type definition)
{
    if (type == null)
        throw new ArgumentNullException("type");
    if (definition == null)
        throw new ArgumentNullException("definition");
    if (!definition.IsGenericTypeDefinition)
        throw new ArgumentException(
            "La définition doit être une définition de type générique", "definition");

    if (definition.IsInterface)
        foreach (var interfaceType in type.GetInterfaces())
            if (interfaceType.IsGenericType
                && interfaceType.GetGenericTypeDefinition() == definition)
                return interfaceType;

    for (Type t = type; t != null; t = t.BaseType)
        if (t.IsGenericType && t.GetGenericTypeDefinition() == definition)
            return t;

    return null;
}

7voto

vexe Points 899

Ajouté à la réponse de @jaredpar, voici ce que j'utilise pour vérifier les interfaces :

public static bool IsImplementerOfRawGeneric(this Type type, Type toCheck)
{
    if (toCheck.GetTypeInfo().IsClass)
    {
        return false;
    }

    return type.GetInterfaces().Any(interfaceType =>
    {
        var current = interfaceType.GetTypeInfo().IsGenericType ?
                    interfaceType.GetGenericTypeDefinition() : interfaceType;
        return current == toCheck;
    });
}

public static bool IsSubTypeOfRawGeneric(this Type type, Type toCheck)
{
    return type.IsInterface ?
          IsImplementerOfRawGeneric(type, toCheck)
        : IsSubclassOfRawGeneric(type, toCheck);
}

Ex :

Console.WriteLine(typeof(IList<>).IsSubTypeOfRawGeneric(typeof(IList))); // true

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X