Wie überprüfe ich, ob ein Typ ein Subtyp oder der Typ eines Objekts ist?

335

Um zu überprüfen, ob ein Typ eine Unterklasse eines anderen Typs in C # ist, ist es einfach:

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

Dies wird jedoch fehlschlagen:

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

Gibt es eine Möglichkeit zu überprüfen, ob ein Typ entweder eine Unterklasse ODER der Basisklasse selbst ist, ohne einen OROperator oder eine Erweiterungsmethode zu verwenden?

Daniel T.
quelle

Antworten:

498

Anscheinend nein.

Hier sind die Optionen:

Type.IsSubclassOf

Wie Sie bereits herausgefunden haben, funktioniert dies nicht, wenn die beiden Typen identisch sind. Hier ist ein Beispiel für ein LINQPad- Programm, das Folgendes demonstriert:

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

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

Ausgabe:

True
False

Was darauf hinweist, dass dies Derivedeine Unterklasse von ist Base, aber das Baseist (offensichtlich) keine Unterklasse von sich.

Type.IsAssignableFrom

Dies wird Ihre spezielle Frage beantworten, aber es wird Ihnen auch falsch positive Ergebnisse liefern. Wie Eric Lippert in den Kommentaren ausgeführt hat, wird die Methode zwar Truefür die beiden oben genannten Fragen zurückkehren, aber auch Truefür diese, die Sie wahrscheinlich nicht wollen:

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

Hier erhalten Sie folgende Ausgabe:

True
True
True

Das letzte Truedort würde anzeigen, wenn die Methode nur die gestellte Frage beantwortet, uint[]die von int[]oder vom gleichen Typ erbt , was eindeutig nicht der Fall ist.

Ist IsAssignableFromalso auch nicht ganz richtig.

is und as

Das "Problem" mit isund asim Kontext Ihrer Frage besteht darin, dass Sie die Objekte bearbeiten und einen der Typen direkt in Code schreiben müssen und nicht mit TypeObjekten arbeiten müssen.

Mit anderen Worten, dies wird nicht kompiliert:

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

noch wird dies:

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

noch wird dies:

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

Fazit

Während die oben genannten Methoden möglicherweise Ihren Anforderungen entsprechen, ist die einzig richtige Antwort auf Ihre Frage (wie ich es sehe), dass Sie eine zusätzliche Prüfung benötigen:

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

was in einer Methode natürlich sinnvoller ist:

public bool IsSameOrSubclass(Type potentialBase, Type potentialDescendant)
{
    return potentialDescendant.IsSubclassOf(potentialBase)
           || potentialDescendant == potentialBase;
}
Lasse V. Karlsen
quelle
2
Vielen Dank! Ich werde dies als die richtige Antwort markieren (ich muss noch 8 Minuten warten), da Sie erwähnt haben, dass die Prüfung rückgängig gemacht werden muss, und einen Link zur MSDN-Dokumentation bereitgestellt haben.
Daniel T.
71
Beachten Sie, dass dies nicht das tut, wonach die Frage gestellt wurde. Dies bestimmt nicht, ob ein Typ eine Unterklasse eines anderen ist, sondern ob ein Typ mit einem anderen kompatibel ist. Ein Array von uint ist keine Unterklasse eines Arrays von int, aber sie sind zuweisungskompatibel. IEnumerable <Giraffe> ist keine Unterklasse von IEnumerable <Animal>, aber sie sind in Version 4 zuweisungskompatibel.
Eric Lippert
Es gibt keine Möglichkeit, dies in Methodennamen wie Java zu tun? `` `void <? erweitert Base> saveObject (? objectToSave) `` `
Oliver Dixon
2
Wie würde das dazu IsInstanceOfTypepassen?
Lennart
Es mag verlockend sein, IsSameOrSubclass in eine Erweiterungsmethode zu konvertieren, aber ich empfehle dagegen, es ist etwas umständlich zu lesen und zu schreiben und leicht durcheinander zu bringen (das Umkehren der Reihenfolge von PotentialBase und PotentialDescendant kann tödlich sein).
jrh
36
typeof(BaseClass).IsAssignableFrom(unknownType);
Marc Gravell
quelle
1

Wenn Sie versuchen, dies in einem Xamarin Forms PCL-Projekt zu tun, geben die oben genannten Lösungen IsAssignableFromeinen Fehler aus:

Fehler: 'Typ' enthält keine Definition für 'IsAssignableFrom' und es konnte keine Erweiterungsmethode 'IsAssignableFrom' gefunden werden, die ein erstes Argument vom Typ 'Typ' akzeptiert (fehlt eine using-Direktive oder eine Assemblyreferenz?)

weil IsAssignableFromnach einem TypeInfoObjekt fragt . Sie können die GetTypeInfo()Methode verwenden von System.Reflection:

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

Bruno Serrano
quelle
0

Ich poste diese Antwort in der Hoffnung, dass jemand mit mir teilt, ob und warum es eine schlechte Idee wäre. In meiner Anwendung habe ich eine Eigenschaft vom Typ Typ, die ich überprüfen möchte, um sicherzustellen, dass es sich um den Typ (A) oder den Typ (B) handelt, wobei B eine von A abgeleitete Klasse ist. Also mein 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;
        }
    }
}

Ich fühle mich hier vielleicht ein bisschen naiv, daher wäre jedes Feedback willkommen.

Baskren
quelle
2
Bei dieser Idee gibt es einige Probleme: 1) Der Typ benötigt einen parameterlosen Konstruktor, oder CreateInstance schlägt fehl. 2) Casting nach (A) gibt nicht null zurück, wenn das Casting nicht möglich ist, es wirft; 3) Sie benötigen die neue Instanz nicht wirklich, haben also eine nutzlose Zuordnung. Die akzeptierte Antwort ist besser (wenn auch nicht perfekt).
Marcel Popescu
Danke für die Rückmeldung. Sehr hilfreich.
Baskren
@baskren, vielleicht dann seinen hilfreichen Kommentar positiv bewerten. Ich war die Nummer 1.
Developer63