En utilisant la réflexion, j'essaie de trouver l'ensemble des types qui héritent d'une classe de base donnée. Il n'a pas fallu longtemps pour trouver des types simples, mais je suis perplexe en ce qui concerne les génériques.
Pour ce morceau de code, le premier IsAssignableFrom retourne vrai, mais le second retourne faux. Et pourtant, l'affectation finale se compile très bien.
class class1 { }
class class2 : class1 { }
class generic1<T> { }
class generic2<T> : generic1<T> { }
class Program
{
static void Main(string[] args)
{
Type c1 = typeof(class1);
Type c2 = typeof(class2);
Console.WriteLine("c1.IsAssignableFrom(c2): {0}", c1.IsAssignableFrom(c2));
Type g1 = typeof(generic1<>);
Type g2 = typeof(generic2<>);
Console.WriteLine("g1.IsAssignableFrom(g2): {0}", g1.IsAssignableFrom(g2));
generic1<class1> cc = new generic2<class1>();
}
}
Alors, comment puis-je déterminer au moment de l'exécution si une définition de type générique est dérivée d'une autre?
De la réponse à une autre question :
public static bool IsAssignableToGenericType(Type givenType, Type genericType)
{
var interfaceTypes = givenType.GetInterfaces();
foreach (var it in interfaceTypes)
{
if (it.IsGenericType && it.GetGenericTypeDefinition() == genericType)
return true;
}
if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
return true;
Type baseType = givenType.BaseType;
if (baseType == null) return false;
return IsAssignableToGenericType(baseType, genericType);
}
(Si vous aimez la réponse, veuillez voter pour la réponse liée car le code n'est pas le mien.)
Le code exact que vous avez publié ne renvoie pas de résultats surprenants.
Cela dit "faux":
Type g1 = typeof(generic1<>);
Type g2 = typeof(generic2<>);
Console.WriteLine("g1.IsAssignableFrom(g2): {0}", g1.IsAssignableFrom(g2));
Cela dit "vrai":
Type g1 = typeof(generic1<class1>);
Type g2 = typeof(generic2<class1>);
Console.WriteLine("g1.IsAssignableFrom(g2): {0}", g1.IsAssignableFrom(g2));
La différence est que les types génériques ouverts ne peuvent pas avoir d'instances, donc l'un n'est pas "assignable" à l'autre.
De la docs :
Renvoie
true
sic
et leType
courant représentent le même type, ou si leType
courant est dans la hiérarchie d'héritage dec
, ou si leType
courant est une interface quec
implémente, ou sic
est un paramètre de type générique et que leType
courant représente une des contraintes dec
.false
si aucune de ces conditions n'est vraie, ou sic
estnull
.
Dans ce cas, clairement aucune de ces conditions n'est vraie. Et il y a une note supplémentaire:
Une définition de type générique n'est pas attribuable à partir d'un type construit fermé. Autrement dit, vous ne pouvez pas affecter le type construit fermé
MyGenericList<int>
(MyGenericList(Of Integer)
dans Visual Basic) à une variable de typeMyGenericList<T>
.
Dans le cas suivant, utilisez la méthode fournie par Konrad Rudolph, comme: IsAssignableToGenericType (typeof (A), typeof (A <>)); // return false
Je pense que voici une meilleure réponse
public static bool IsAssignableFrom(Type extendType, Type baseType)
{
while (!baseType.IsAssignableFrom(extendType))
{
if (extendType.Equals(typeof(object)))
{
return false;
}
if (extendType.IsGenericType && !extendType.IsGenericTypeDefinition)
{
extendType = extendType.GetGenericTypeDefinition();
}
else
{
extendType = extendType.BaseType;
}
}
return true;
}
le cas de test, voir tilisation d'IsAssignableFrom avec des génériques C # pour plus de détails
using System;
/**
* Sam Sha - yCoder.com
*
* */
namespace Test2
{
class MainClass
{
public static void Main (string[] args)
{
string a = "ycoder";
Console.WriteLine(a is object);
A aa = new A();
//Console.WriteLine(aa is A<>);//con't write code like this
typeof(A<>).IsAssignableFrom(aa.GetType());//return false
Trace(typeof(object).IsAssignableFrom(typeof(string)));//true
Trace(typeof(A<>).IsAssignableFrom(typeof(A)));//false
AAA aaa = new AAA();
Trace("Use IsTypeOf:");
Trace(IsTypeOf(aaa, typeof(A<>)));
Trace(IsTypeOf(aaa, typeof(AA)));
Trace(IsTypeOf(aaa, typeof(AAA<>)));
Trace("Use IsAssignableFrom from stackoverflow - not right:");
Trace(IsAssignableFrom(typeof(A), typeof(A<>))); // error
Trace(IsAssignableFrom(typeof(AA), typeof(A<>)));
Trace(IsAssignableFrom(typeof(AAA), typeof(A<>)));
Trace("Use IsAssignableToGenericType:");
Trace(IsAssignableToGenericType(typeof(A), typeof(A<>)));
Trace(IsAssignableToGenericType(typeof(AA), typeof(A<>)));
Trace(IsAssignableToGenericType(typeof(AAA), typeof(A<>)));
}
static void Trace(object log){
Console.WriteLine(log);
}
public static bool IsTypeOf(Object o, Type baseType)
{
if (o == null || baseType == null)
{
return false;
}
bool result = baseType.IsInstanceOfType(o);
if (result)
{
return result;
}
return IsAssignableFrom(o.GetType(), baseType);
}
public static bool IsAssignableFrom(Type extendType, Type baseType)
{
while (!baseType.IsAssignableFrom(extendType))
{
if (extendType.Equals(typeof(object)))
{
return false;
}
if (extendType.IsGenericType && !extendType.IsGenericTypeDefinition)
{
extendType = extendType.GetGenericTypeDefinition();
}
else
{
extendType = extendType.BaseType;
}
}
return true;
}
//from stackoverflow - not good enough
public static bool IsAssignableToGenericType(Type givenType, Type genericType) {
var interfaceTypes = givenType.GetInterfaces();
foreach (var it in interfaceTypes)
if (it.IsGenericType)
if (it.GetGenericTypeDefinition() == genericType) return true;
Type baseType = givenType.BaseType;
if (baseType == null) return false;
return baseType.IsGenericType &&
baseType.GetGenericTypeDefinition() == genericType ||
IsAssignableToGenericType(baseType, genericType);
}
}
class A{}
class AA : A{}
class AAA : AA{}
}
Vous devez comparer le type contenu. Voir: Comment obtenir le type de T d'un membre d'une classe ou d'une méthode générique?
En d'autres termes, je pense que vous devez vérifier si le type contenu par la classe générique est assignable plutôt que la classe générique elle-même.
J'ai une approche différente qui résout ce problème, voici mes cours
public class Signal<T>{
protected string Id {get; set;} //This must be here, I use a property because MemberInfo is returned in an array via GetMember() reflection function
//Some Data and Logic And stuff that involves T
}
public class OnClick : Signal<string>{}
Maintenant, si j'ai une instance de type OnClick mais je ne le sais pas, et je veux savoir si j'ai une instance de quelque chose qui hérite de Signal <> de tout type? je fais ça
Type type = GetTypeWhomISuspectMightBeAGenericSignal();
PropertyInfo secretProperty = type.GetProperty("Id", BindingFlags.NonPublic | BindingFlags.Instance);
Type SpecificGenericType = secretProperty.DeclaringType; //This is the trick
bool IsMyTypeInheriting = SpecificGenericType.IsGenericType && SpecificGenericType.GetGenericTypeDefinition() == typeof(Signal<>); //This way we are getting the genericTypeDefinition and comparing it to any other genericTypeDefinition of the same argument length.
Donc, cela fonctionne pour moi, ce n'est pas récursif, et il utilise une astuce via une propriété désignée. Il a des limites qu'il est difficile d'écrire une fonction qui vérifie l'assignation pour tous les génériques. Mais pour un type spécifique, cela fonctionne
De toute évidence, vous devez vérifier si les conditions () sont meilleures et autres, mais ce sont les lignes brutes requises pour évaluer l'assignation d'un type à son générique de base, de cette façon.
J'espère que cela t'aides