web-dev-qa-db-fra.com

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

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 une sous-classe OR de la classe de base elle-même, sans utiliser d'opérateur OR ni de méthode d'extension?

312
Daniel T.

Apparemment non.

Voici les options:

Type.IsSubclassOf

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

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

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

Sortie:

True
False

Ce qui indique que Derived est une sous-classe de Base, mais que Base n'est (évidemment) pas une sous-classe de lui-même.

Type.IsAssignableFrom

Maintenant, cela répondra à votre question particulière, mais cela vous donnera également des faux positifs. Comme Eric Lippert l'a souligné dans les commentaires, bien que la méthode retourne bien True pour les deux questions ci-dessus, elle retournera également True pour 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 le résultat suivant:

True
True
True

Le dernier True indiquera, si la méthode uniquement répond à la question posée, que uint[] hérite de int[] ou qu'ils soient du même type, ce qui n'est clairement pas le cas.

Donc, IsAssignableFrom n'est pas tout à fait correct non plus.

is et as

Le "problème" avec is et as dans le contexte de votre question est qu’ils vous demanderont d’opérer sur les objets et d’écrire l’un des types directement dans le code et de ne pas travailler avec Type objets.

En d'autres termes, cela ne compilera pas:

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

cela ne va pas non plus:

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

cela ne va pas non plus:

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 répondent à vos besoins, la seule réponse correcte à votre question (selon moi) est que vous aurez besoin d'une vérification supplémentaire:

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

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

public bool IsSameOrSubclass(Type potentialBase, Type potentialDescendant)
{
    return potentialDescendant.IsSubclassOf(potentialBase)
           || potentialDescendant == potentialBase;
}
460
typeof(BaseClass).IsAssignableFrom(unknownType);
30
Marc Gravell

Vous devriez essayer d'utiliser Type.IsAssignableFrom .

9
Thomas

Si vous essayez de le faire dans un projet PCL Xamarin Forms, les solutions ci-dessus utilisant IsAssignableFrom donnent 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 (il vous manque une directive using ou une référence Assembly?)

parce que IsAssignableFrom demande un objet TypeInfo. Vous pouvez utiliser la méthode GetTypeInfo() à partir de System.Reflection:

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

1
BrunoSerrano

Je poste cette réponse dans l'espoir que quelqu'un me dise si et pourquoi ce serait une mauvaise idée. Dans mon application, je souhaite vérifier une propriété de Type pour m'assurer qu'elle est bien typeof (A) ou typeof (B), où B est une 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.

0
baskren