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