Comment vérifier si un type est un sous-type OU le type d'un objet?

335

Pour vérifier si un type est une sous-classe d'un autre type en C #, c'est simple:

typeof (SubClass).IsSubclassOf(typeof (BaseClass)); // returns true

Cependant, cela échouera:

typeof (BaseClass).IsSubclassOf(typeof (BaseClass)); // returns false

Existe-t-il un moyen de vérifier si un type est soit une sous-classe OU de la classe de base elle-même, sans utiliser d' ORopérateur ni utiliser une méthode d'extension?

Daniel T.
la source

Réponses:

499

Apparemment non.

Voici les options:

Type.IsSubclassOf

Comme vous l'avez déjà découvert, cela ne fonctionnera pas si les deux types sont identiques, voici un exemple de programme LINQPad qui montre:

void Main()
{
    typeof(Derived).IsSubclassOf(typeof(Base)).Dump();
    typeof(Base).IsSubclassOf(typeof(Base)).Dump();
}

public class Base { }
public class Derived : Base { }

Production:

True
False

Ce qui indique qu'il Deriveds'agit d'une sous-classe de Base, mais ce Basen'est (évidemment) pas une sous-classe en soi.

Type.IsAssignableFrom

Maintenant, cela répondra à votre question particulière, mais cela vous donnera également de faux positifs. Comme Eric Lippert l'a souligné dans les commentaires, bien que la méthode revienne effectivement Truepour les deux questions ci-dessus, elle reviendra également Truepour celles-ci, ce que vous ne voulez probablement pas:

void Main()
{
    typeof(Base).IsAssignableFrom(typeof(Derived)).Dump();
    typeof(Base).IsAssignableFrom(typeof(Base)).Dump();
    typeof(int[]).IsAssignableFrom(typeof(uint[])).Dump();
}

public class Base { }
public class Derived : Base { }

Ici, vous obtenez la sortie suivante:

True
True
True

La dernière Truelà-bas indiquerait, si la méthode ne répondait qu'à la question posée, qu'elle uint[]hérite int[]ou qu'elle est du même type, ce qui n'est clairement pas le cas.

Ce IsAssignableFromn'est donc pas tout à fait correct non plus.

is et as

Le "problème" avec iset asdans le contexte de votre question est qu'ils vous obligeront à opérer sur les objets et à écrire l'un des types directement dans le code, et non à travailler avec des Typeobjets.

En d'autres termes, cela ne compilera pas:

SubClass is BaseClass
^--+---^
   |
   +-- need object reference here

ni cela:

typeof(SubClass) is typeof(BaseClass)
                    ^-------+-------^
                            |
                            +-- need type name here, not Type object

ni cela:

typeof(SubClass) is BaseClass
^------+-------^
       |
       +-- this returns a Type object, And "System.Type" does not
           inherit from BaseClass

Conclusion

Bien que les méthodes ci-dessus puissent répondre à vos besoins, la seule réponse correcte à votre question (telle que je la vois) est que vous aurez besoin d'une vérification supplémentaire:

typeof(Derived).IsSubclassOf(typeof(Base)) || typeof(Derived) == typeof(Base);

ce qui a bien sûr plus de sens dans une méthode:

public bool IsSameOrSubclass(Type potentialBase, Type potentialDescendant)
{
    return potentialDescendant.IsSubclassOf(potentialBase)
           || potentialDescendant == potentialBase;
}
Lasse V. Karlsen
la source
2
Merci! Je vais marquer cela comme la bonne réponse (je dois attendre 8 minutes de plus) car vous avez mentionné que la vérification doit être inversée et fourni un lien vers la documentation MSDN.
Daniel T.
71
Notez que cela ne fait pas vraiment ce que la question demandait; cela ne détermine pas si un type est une sous-classe d'un autre, mais plutôt si un type est compatible avec une autre affectation. Un tableau de uint n'est pas une sous-classe d'un tableau d'int, mais ils sont compatibles avec les affectations. IEnumerable <Giraffe> n'est pas une sous-classe de IEnumerable <Animal>, mais ils sont compatibles avec les affectations dans v4.
Eric Lippert
Il n'y a aucun moyen de le faire dans un nom de méthode comme Java? `` vide <? étend Base> saveObject (? objectToSave) ``
Oliver Dixon
2
Comment cela IsInstanceOfTypes'intégrerait-il?
Lennart
Il peut être tentant de convertir IsSameOrSubclass en une méthode d'extension, mais je le déconseille, c'est un peu gênant à lire et à écrire, et facile à gâcher (inverser l'ordre de potentialBase et potentialDescendant peut être mortel).
2017
36
typeof(BaseClass).IsAssignableFrom(unknownType);
Marc Gravell
la source
1

Si vous essayez de le faire dans un projet PCL Xamarin Forms, les solutions ci-dessus utilisant IsAssignableFromdonnent une erreur:

Erreur: 'Type' ne contient pas de définition pour 'IsAssignableFrom' et aucune méthode d'extension 'IsAssignableFrom' acceptant un premier argument de type 'Type' n'a pu être trouvée (vous manque une directive using ou une référence d'assembly?)

car IsAssignableFromdemande un TypeInfoobjet. Vous pouvez utiliser la GetTypeInfo()méthode à partir de System.Reflection:

typeof(BaseClass).GetTypeInfo().IsAssignableFrom(typeof(unknownType).GetTypeInfo())

Bruno Serrano
la source
0

Je poste cette réponse avec l'espoir que quelqu'un partage avec moi si et pourquoi ce serait une mauvaise idée. Dans mon application, j'ai une propriété de Type que je veux vérifier pour être sûr que c'est typeof (A) ou typeof (B), où B est n'importe quelle classe dérivée de A. Donc mon code:

public class A
{
}

public class B : A
{
}

public class MyClass
{
    private Type _helperType;
    public Type HelperType
    {
        get { return _helperType; }
        set 
        {
            var testInstance = (A)Activator.CreateInstance(value);
            if (testInstance==null)
                throw new InvalidCastException("HelperType must be derived from A");
            _helperType = value;
        }
    }
}

Je sens que je pourrais être un peu naïf ici, donc tout commentaire serait le bienvenu.

baskren
la source
2
Il y a quelques problèmes avec cette idée: 1) le type a besoin d'un constructeur sans paramètre ou CreateInstance échouera; 2) lancer sur (A) ne retourne pas nul si le lancer ne peut pas être fait, il lance; 3) vous n'avez pas réellement besoin de la nouvelle instance, vous avez donc une allocation inutile. La réponse acceptée est meilleure (mais pas parfaite).
Marcel Popescu
Merci pour les commentaires. Très utile.
baskren
@baskren, peut-être ensuite voter pour son commentaire utile. J'étais upvote # 1.
Developer63