J'ai une classe générique dans mon projet avec des classes dérivées.
public class GenericClass<T> : GenericInterface<T>
{
}
public class Test : GenericClass<SomeType>
{
}
Existe-t-il un moyen de savoir si un Type
objet est dérivé GenericClass
?
t.IsSubclassOf(typeof(GenericClass<>))
ne marche pas.
c#
generics
reflection
bernhardrusch
la source
la source
(Republié en raison d'une réécriture massive)
La réponse du code de JaredPar est fantastique, mais j'ai une astuce qui la rendrait inutile si vos types génériques ne sont pas basés sur des paramètres de type valeur. J'étais accroché sur la raison pour laquelle l'opérateur "is" ne fonctionnerait pas, j'ai donc également documenté les résultats de mon expérimentation pour référence future. Veuillez améliorer cette réponse pour améliorer encore sa clarté.
POINTE:
Si vous vous assurez que votre implémentation GenericClass hérite d'une classe de base non générique abstraite telle que GenericClassBase, vous pouvez poser la même question sans aucun problème comme ceci:
IsSubclassOf ()
Mes tests indiquent que IsSubclassOf () ne fonctionne pas sur les types génériques sans paramètre tels que
alors qu'il fonctionnera avec
Par conséquent, le code suivant fonctionnera pour toute dérivation de GenericClass <>, en supposant que vous êtes prêt à tester en fonction de SomeType:
La seule fois où je peux imaginer que vous voudriez tester par GenericClass <> est dans un scénario de cadre de plug-in.
Réflexions sur l'opérateur "is"
Au moment de la conception, C # ne permet pas l'utilisation de génériques sans paramètre car ils ne sont pas essentiellement un type CLR complet à ce stade. Par conséquent, vous devez déclarer des variables génériques avec des paramètres, et c'est pourquoi l'opérateur "is" est si puissant pour travailler avec des objets. Par ailleurs, l'opérateur "is" ne peut pas non plus évaluer les types génériques sans paramètre.
L'opérateur «is» testera toute la chaîne d'héritage, y compris les interfaces.
Donc, étant donné une instance de n'importe quel objet, la méthode suivante fera l'affaire:
C'est en quelque sorte redondant, mais j'ai pensé que j'irais de l'avant et le visualiserais pour tout le monde.
Donné
Les lignes de code suivantes renverraient true:
D'un autre côté, si vous voulez quelque chose de spécifique à GenericClass, vous pouvez le rendre plus spécifique, je suppose, comme ceci:
Ensuite, vous testeriez comme ceci:
la source
Type
objet.typeof
opérateur. Selon les documents: "L'opérateur typeof est utilisé pour obtenir l'objet System.Type pour un type."J'ai travaillé sur certains de ces échantillons et j'ai constaté qu'ils manquaient dans certains cas. Cette version fonctionne avec toutes sortes de génériques: types, interfaces et définitions de type de ceux-ci.
Voici également les tests unitaires:
la source
!parent.IsGenericType || parent.GetGenericTypeDefinition() == parent;
vous remplacez donc cette variable par l'expansion de l'instruction if:if (parent.IsGenericType && shouldUseGenericType)
et vous obtenezif (parent.IsGenericType && (!parent.IsGenericType || parent.GetGenericTypeDefinition() == parent))
ce qui se réduit alors àif (parent.IsGenericType && parent.GetGenericTypeDefinition() == parent)) parent = parent.GetGenericTypeDefinition();
int j = 0;
if (j is an int && j == 0)
{ j=0; }
Est-ce que je reçois mes références égales et mes valeurs mélangées? Je pensais qu'il n'y avait qu'une seule instance de chaque type en mémoire, donc deux variables qui pointent vers le même type pointent en fait vers le même emplacement en mémoire.Il me semble que cette implémentation fonctionne dans plus de cas (classe générique et interface avec ou sans paramètres initiés, quel que soit le nombre d'enfants et de paramètres):
Voici mes
7076 cas de test:Classes et interfaces de test:
la source
Le code de JaredPar fonctionne mais uniquement pour un niveau d'héritage. Pour des niveaux d'héritage illimités, utilisez le code suivant
la source
while
code de JaredPar couvre des niveaux illimités.Voici une petite méthode que j'ai créée pour vérifier qu'un objet est dérivé d'un type spécifique. Fonctionne très bien pour moi!
la source
Cela peut être exagéré, mais j'utilise des méthodes d'extension comme les suivantes. Ils vérifient les interfaces ainsi que les sous-classes. Il peut également renvoyer le type qui a la définition générique spécifiée.
Par exemple pour l'exemple dans la question, il peut tester aussi bien l'interface générique que la classe générique. Le type renvoyé peut être utilisé avec
GetGenericArguments
pour déterminer que le type d'argument générique est "SomeType".la source
En s'appuyant sur l'excellente réponse ci-dessus de fir3rpho3nixx et David Schmitt, j'ai modifié leur code et ajouté le test ShouldInheritOrImplementTypedGenericInterface (le dernier).
la source
Tout cela peut être fait facilement avec linq. Cela trouvera tous les types qui sont une sous-classe de la classe de base générique GenericBaseType.
la source
Solution simple: il suffit de créer et d'ajouter une deuxième interface non générique à la classe générique:
Ensuite , il suffit de vérifier pour cela de quelque façon que vous aimez utiliser
is
,as
,IsAssignableFrom
, etc.Évidemment, cela n'est possible que si vous avez la possibilité d'éditer la classe générique (que l'OP semble avoir), mais c'est un peu plus élégant et lisible que d'utiliser une méthode d'extension cryptique.
la source
IGenericClass
ne vous garantira pasGenericClass
ouGenericInterface
est réellement étendu ou implémenté. Cela signifie que votre compilateur ne vous permettra pas non plus d'accéder aux membres de la classe générique.Ajouté à la réponse de @ jaredpar, voici ce que j'utilise pour vérifier les interfaces:
Ex:
la source
JaredPar,
Cela n'a pas fonctionné pour moi si je passe typeof (type <>) à toCheck. Voici ce que j'ai changé.
la source
typeof(type<>)
astoCheck
. De plus, vous avez vraiment besoin de la vérification nulle comme dans la solution de JaredPar. De plus, je ne sais pas quoi faire d'autre en remplaçantcur == generic
sa solution parcur.IsGenericType && generic.GetGenericTypeDefinition() == cur.GetGenericTypeDefinition()
autre chose que de restreindre votre méthode pour qu'elle ne fonctionne que pour les types génériques. En d'autres termes, quelque chose comme ça échoue avec une exception:IsSubclassOfRawGeneric(typeof(MyClass), typeof(MyClass<>))
@EnocNRoll - La réponse d'Ananda Gopal est intéressante, mais dans le cas où une instance n'est pas instanciée à l'avance ou si vous souhaitez vérifier avec une définition de type générique, je suggère cette méthode:
et l'utiliser comme:
Il y a quatre cas conditionnels lorsque les deux
t
(à tester) etd
sont des types génériques et deux cas sont couverts part==d
qui sont (1) ni ,t
nid
une définition générique ou (2) les deux sont des définitions génériques . Les autres cas sont l'un d'eux est une définition générique, c'est seulement quandd
est déjà une définition générique que nous avons la chance de dire que at
est ad
mais pas l'inverse.Il devrait fonctionner avec des classes ou des interfaces arbitraires que vous souhaitez tester, et renvoie ce que si vous testez une instance de ce type avec l'
is
opérateur.la source
la source
Vous pouvez essayer cette extension
la source
tard dans le jeu à ce sujet ... moi aussi j'ai encore une autre permutation de la réponse de JarodPar.
voici Type.IsSubClassOf (Type) avec la permission du réflecteur:
à partir de cela, nous voyons qu'il ne fait rien de trop cray cray et est similaire à l'approche itérative de JaredPar. jusqu'ici tout va bien. voici ma version (avis de non-responsabilité: pas complètement testé, alors laissez-moi savoir si vous rencontrez des problèmes)
Fondamentalement, il s'agit simplement d'une méthode d'extension à System.Type - je l'ai fait pour limiter intentionnellement le type "thisType" aux types concrets, car mon utilisation immédiate est la requête LINQ "où" les prédicats contre les objets Type. Je suis sûr que tous les gens intelligents pourraient le transformer en une méthode statique efficace et polyvalente si vous en avez besoin :) le code fait quelques choses que le code de la réponse ne fait pas
le reste est fondamentalement le même que le code de JaredPar
la source