Jak sprawdzić, czy typ jest podtypem LUB typem obiektu?

335

Aby sprawdzić, czy typ jest podklasą innego typu w języku C #, łatwo:

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

Jednak to się nie powiedzie:

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

Czy istnieje sposób sprawdzenia, czy typ jest podklasą OR samej klasy bazowej, bez użycia ORoperatora lub metody rozszerzenia?

Daniel T.
źródło

Odpowiedzi:

499

Najwyraźniej nie.

Oto opcje:

Type.IsSubclassOf

Jak już się dowiedziałeś, to nie zadziała, jeśli oba typy są takie same, oto przykładowy program LINQPad , który pokazuje:

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

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

Wynik:

True
False

Co wskazuje, że Derivedjest to podklasa Base, ale nie Basejest (oczywiście) sama w sobie podklasą.

Type.IsAssignableFrom

To odpowie na twoje konkretne pytanie, ale da ci również fałszywe pozytywne wyniki. Jak zauważył Eric Lippert w komentarzach, metoda rzeczywiście powróci Truedo dwóch powyższych pytań, ale wróci również Truedo tych, których prawdopodobnie nie chcesz:

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

Tutaj otrzymujesz następujące dane wyjściowe:

True
True
True

Ostatni Truewskazywałby, gdyby metoda odpowiedziała tylko na zadane pytanie, że uint[]dziedziczy int[]lub jest tego samego typu, co oczywiście nie jest prawdą.

To też IsAssignableFromnie jest do końca poprawne.

is i as

„Problem” z isi asw związku z pytaniem jest to, że wymagają one, aby działać na obiektach i napisać jeden z typów bezpośrednio w kodzie, a nie pracy z Typeobiektami.

Innymi słowy, to się nie skompiluje:

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

nie będzie to również:

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

nie będzie to również:

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

Wniosek

Chociaż powyższe metody mogą pasować do twoich potrzeb, jedyną prawidłową odpowiedzią na twoje pytanie (jak widzę) jest to, że będziesz potrzebować dodatkowej kontroli:

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

co oczywiście ma większy sens w metodzie:

public bool IsSameOrSubclass(Type potentialBase, Type potentialDescendant)
{
    return potentialDescendant.IsSubclassOf(potentialBase)
           || potentialDescendant == potentialBase;
}
Lasse V. Karlsen
źródło
2
Dzięki! Oznaczę to jako poprawną odpowiedź (muszę poczekać 8 minut), ponieważ wspomniałeś, że czek musi zostać odwrócony i podałeś link do dokumentacji MSDN.
Daniel T.
71
Zauważ, że tak naprawdę nie robi to, o co zadawane pytanie; nie określa to, czy jeden typ jest podklasą innego, ale raczej czy jeden typ jest przypisany zgodny z innym. Tablica uint nie jest podklasą tablicy int, ale są one kompatybilne z przypisaniami. IEnumerable <Giraffe> nie jest podklasą IEnumerable <Animal>, ale są one kompatybilne z przypisywaniem w v4.
Eric Lippert
Nie można tego zrobić w nazwie metody takiej jak Java? `` void <? rozszerza Base> saveObject (? objectToSave) ``
Oliver Dixon
2
Jak by to IsInstanceOfTypepasowało?
Lennart
Przekształcanie IsSameOrSubclass w metodę rozszerzenia może być kuszące, ale odradzam, jest to trochę niewygodne w czytaniu i pisaniu, a także łatwe do zepsucia (odwrócenie kolejności potencjalnychBaseów i potencjalnychDescendantów może być śmiertelne).
jrh
36
typeof(BaseClass).IsAssignableFrom(unknownType);
Marc Gravell
źródło
1

Jeśli próbujesz to zrobić w projekcie PCL Xamarin Forms, powyższe rozwiązania używają IsAssignableFrombłędu:

Błąd: „Typ” nie zawiera definicji „IsAssignableFrom” i nie można znaleźć metody rozszerzenia „IsAssignableFrom” akceptującej pierwszy argument typu „Typ” (czy brakuje dyrektywy używającej lub odwołania do zestawu?)

ponieważ IsAssignableFromprosi o TypeInfoobiekt. Możesz użyć GetTypeInfo()metody z System.Reflection:

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

Bruno Serrano
źródło
0

Publikuję tę odpowiedź z nadzieją, że ktoś się ze mną podzieli, czy i dlaczego byłby to zły pomysł. W mojej aplikacji mam właściwość Type, którą chcę sprawdzić, aby upewnić się, że jest to typeof (A) lub typeof (B), gdzie B to dowolna klasa wywodząca się z A. Więc mój kod:

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

Wydaje mi się, że jestem tu trochę naiwny, więc wszelkie opinie będą mile widziane.

baskren
źródło
2
Z tym pomysłem wiąże się kilka problemów: 1) typ wymaga konstruktora bez parametrów lub CreateInstance zawiedzie; 2) rzutowanie na (A) nie zwraca wartości null, jeśli rzutowanie nie może być wykonane, rzuca; 3) tak naprawdę nie potrzebujesz nowej instancji, więc masz niepotrzebny przydział. Przyjęta odpowiedź jest lepsza (choć nie idealna).
Marcel Popescu,
Dzięki za opinie. Bardzo pomocny.
baskren
@baskren, być może wtedy głosujesz na jego pomocny komentarz. Byłem pozytywnie oceniany nr 1.
Developer63