Sprawdź, czy „T” dziedziczy lub implementuje klasę / interfejs

92

Czy istnieje sposób na sprawdzenie, czy T dziedziczy / implementuje klasę / interfejs?

private void MyGenericClass<T> ()
{
    if(T ... inherits or implements some class/interface
}
user1229895
źródło
4
wydaje się, że to działa ... jeśli (typeof (TestClass) .IsAssignableFrom (typeof (T))), czy ktoś mógłby potwierdzić moje podejrzenia? dzięki!
user1229895
Jestem absolutnie pewien, że ta odpowiedź jest wielokrotnie powtarzana!
Felix K.
3
Felix K Nawet jeśli ta odpowiedź była powtarzana wiele razy, to również pomaga wielu facetom wiele razy;) ... jak ja pięć minut temu :)
Samuel

Odpowiedzi:

136

Istnieje metoda o nazwie Type.IsAssignableFrom () .

Aby sprawdzić, czy Tdziedziczy / implementuje Employee:

typeof(Employee).IsAssignableFrom(typeof(T));

Jeśli kierujesz .NET Core, metoda została przeniesiona do TypeInfo:

typeof(Employee).GetTypeInfo().IsAssignableFrom(typeof(T).Ge‌​tTypeInfo())
nikeee
źródło
powinieneś zaktualizować swoją odpowiedź przykładem, np. typeof (T) .IsAssignableFrom (typeof (IMyInterface))
Dr Andrew Burnett-Thompson
Nie skończone nikeee; stara odpowiedź wciąż istnieje. :) Zajęło mi kilka sekund, aby dowiedzieć się, co jest nie tak. W każdym razie +1, znowu fajna funkcja frameworka .net.
Samuel
W rzeczywistości sposób, w jaki wspominasz, jest taki, jaki miałem jakiś czas temu. Poprawiłem ten. Zobacz poprzednie komentarze. T inherits Ufaktycznie przekłada się na typeof(T).IsAssignableFrom(typeof(U)).
nikeee
3
Chociaż to prawie działa, istnieje problem polegający na tym, że jeśli Tjest ograniczony do innego typu TOther, to po wykonaniu w typeof(T)rzeczywistości zostanie oszacowany, typeof(TOther)a nie typ T, który faktycznie przeszedłeś, iw takim przypadku typeof(SomeInterface).IsAssignableFrom(typeof(T))zakończy się niepowodzeniem (zakładając, TOtherże również nie implementuje SomeInterface), nawet jeśli twój konkretny typ zaimplementował SomeInterface.
Dave Cousineau
1
W NET rdzenia IsAssignableFromz TypeInfoklasy przyjmuje tylko TypeInfo jak to tylko argumentu więc próbki powinny być następujące:typeof(Employee).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo())
KA
16

Jeśli chcesz sprawdzić podczas kompilacji: Błąd, jeśli T NIE implementuje żądanego interfejsu / klasy, możesz użyć następującego ograniczenia

public void MyRestrictedMethod<T>() where T : MyInterface1, MyInterface2, MySuperClass
{
    //Code of my method here, clean without any check for type constraints.
}

Mam nadzieję że to pomogło.

snajahi
źródło
12

Prawidłowa składnia to

typeof(Employee).IsAssignableFrom(typeof(T))

Dokumentacja

Wartość zwracana: true jeśli ca prąd Typereprezentują ten sam typ, lub jeśli obecny Typejest w hierarchii dziedziczenia c, lub jeśli obecny Typejest interface, że cnarzędzia, czy cto ogólny parametr typu i obecny Typestanowi jedno z ograniczeń club if creprezentuje typ wartości, a bieżący Typereprezentuje Nullable<c>( Nullable(Of c)w Visual Basic). falsejeśli żaden z tych warunków nie jest spełniony truelub jeśli ctak null.

źródło

Wyjaśnienie

Jeśli Employee IsAssignableFrom Tto Tdziedziczy z Employee.

Użycie

typeof(T).IsAssignableFrom(typeof(Employee)) 

zwraca true tylko wtedy, gdy albo

  1. Ti Employeereprezentują ten sam typ; lub,
  2. Employeedziedziczy z T.

Może to być zamierzone użycie w niektórych przypadkach, ale w przypadku pierwotnego pytania (i bardziej powszechnego użycia), aby określić, kiedy Tdziedziczy lub implementuje niektóre class/ interface, użyj:

typeof(Employee).IsAssignableFrom(typeof(T))
Łukasz
źródło
9

Wszyscy tak naprawdę mają na myśli:

typeof(BaseType).IsAssignableFrom(typeof(DerivedType)) // => true

ponieważ możesz dosłownie przypisać z instancji a DerivedTypedo instancji a BaseType:

DerivedType childInstance = new DerivedType();
BaseType parentInstance = childInstance; // okay, assigning base from derived
childInstance = (DerivedType) parentInstance; // not okay, assigning derived from base

gdy

public class BaseType {}
public class DerivedType : BaseType {}

I kilka konkretnych przykładów, jeśli masz problem ze zrozumieniem tego:

(przez LinqPad, stąd HorizontalRuni Dump)

void Main()
{
    // http://stackoverflow.com/questions/10718364/check-if-t-inherits-or-implements-a-class-interface

    var b1 = new BaseClass1();

    var c1 = new ChildClass1();
    var c2 = new ChildClass2();
    var nb = new nobase();

    Util.HorizontalRun(
        "baseclass->baseclass,child1->baseclass,baseclass->child1,child2->baseclass,baseclass->child2,nobase->baseclass,baseclass->nobase",
        b1.IsAssignableFrom(typeof(BaseClass1)),
        c1.IsAssignableFrom(typeof(BaseClass1)),
        b1.IsAssignableFrom(typeof(ChildClass1)),
        c2.IsAssignableFrom(typeof(BaseClass1)),
        b1.IsAssignableFrom(typeof(ChildClass2)),
        nb.IsAssignableFrom(typeof(BaseClass1)),
        b1.IsAssignableFrom(typeof(nobase))
        ).Dump("Results");

    var results = new List<string>();
    string test;

    test = "c1 = b1";
    try {
        c1 = (ChildClass1) b1;
        results.Add(test);
    } catch { results.Add("FAIL: " + test); }

    test = "b1 = c1";
    try {
        b1 = c1;
        results.Add(test);
    } catch { results.Add("FAIL: " + test); }

    test = "c2 = b1";
    try {
        c2 = (ChildClass2) b1;
        results.Add(test);
    } catch { results.Add("FAIL: " + test); }

    test = "b1 = c2";
    try {
        b1 = c2;
        results.Add(test);
    } catch { results.Add("FAIL: " + test); }

    results.Dump();
}

// Define other methods and classes here
public static class exts {
    public static bool IsAssignableFrom<T>(this T entity, Type baseType) {
        return typeof(T).IsAssignableFrom(baseType);
    }
}


class BaseClass1 {
    public int id;
}

class ChildClass1 : BaseClass1 {
    public string name;
}

class ChildClass2 : ChildClass1 {
    public string descr;
}

class nobase {
    public int id;
    public string name;
    public string descr;
}

Wyniki

baseclass-> baseclass

Prawdziwe

child1-> baseclass

Fałszywy

baseclass-> child1

Prawdziwe

child2-> baseclass

Fałszywy

baseclass-> child2

Prawdziwe

nobase-> baseclass

Fałszywy

baseclass-> nobase

Fałszywy

i

  • BŁĄD: c1 = b1
  • b1 = c1
  • BŁĄD: c2 = b1
  • b1 = c2
drzaus
źródło
2

Uważam, że składnia to: typeof(Employee).IsAssignableFrom(typeof(T));

Kredowy
źródło
To jest zamierzona składnia. +1
Luke
0

Chociaż IsAssignableFrom jest najlepszym sposobem, jak powiedzieli inni, jeśli potrzebujesz tylko sprawdzić, czy klasa dziedziczy po innej, również typeof(T).BaseType == typeof(SomeClass)wykonuje zadanie.

Jed
źródło
To działa, chyba że SomeClassnie pochodzi bezpośrednio z BaseClass.
Suncat2000
0

Alternatywnymi sposobami stwierdzenia, czy obiekt odziedziczy klasę lub implementuje interfejs, jest użycie isias operatorów .

Jeśli chcesz wiedzieć tylko, czy obiekt dziedziczy klasę lub implementuje interfejs, isoperator zwróci wynik boolowski:

bool isCompatibleType = (o is BaseType || o is IInterface);

Jeśli po teście chcesz użyć dziedziczonej klasy lub zaimplementowanego interfejsu, asoperator wykona bezpieczne rzutowanie, zwracając odwołanie do odziedziczonej klasy lub zaimplementowanego interfejsu, jeśli jest zgodny lub zerowy, jeśli nie jest zgodny:

BaseType b = o as BaseType; // Null if d does not inherit from BaseType.

IInterface i = o as IInterface; // Null if d does not implement IInterface.

Jeśli masz tylko typ T, użyj odpowiedzi @ nikeee.

Suncat2000
źródło