Überprüfen Sie, ob eine Klasse von einer generischen Klasse abgeleitet ist

309

Ich habe eine generische Klasse in meinem Projekt mit abgeleiteten Klassen.

public class GenericClass<T> : GenericInterface<T>
{
}

public class Test : GenericClass<SomeType>
{
}

Gibt es eine Möglichkeit herauszufinden, ob ein TypeObjekt abgeleitet ist GenericClass?

t.IsSubclassOf(typeof(GenericClass<>))

funktioniert nicht.

bernhardrusch
quelle

Antworten:

430

Versuchen Sie diesen Code

static bool IsSubclassOfRawGeneric(Type generic, Type toCheck) {
    while (toCheck != null && toCheck != typeof(object)) {
        var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
        if (generic == cur) {
            return true;
        }
        toCheck = toCheck.BaseType;
    }
    return false;
}
JaredPar
quelle
4
Das ist ein süßer Code, muss ich sagen. Die while-Schleifenimplementierung vermeidet auch die unnötige Beeinträchtigung der Rekursionsleistung. Es ist eine elegante und schöne Lösung für eine meta-generische Frage.
EnocNRoll - AnandaGopal Pardue
2
Ich habe diese Methode meiner statischen ReflectionUtils-Klasse in meinem Framework hinzugefügt und sie auch als Erweiterungsmethode für Objekte angepasst, indem ich toCheck innerhalb der Methode als Type toCheck = obj.GetType () definiert habe. gegeben "dieses Objekt obj" ist der erste Parameter.
EnocNRoll - AnandaGopal Pardue
11
Die while-Schleife wird nicht unterbrochen, wenn der toCheck-Typ keine Klasse ist (dh Schnittstelle). Dies führt zu einer NullReferenceException.
JD Courtoy
2
Dies gibt auch true zurück, wenn toCheck der von Ihnen gesuchte generische Typ ist.
Oillio
14
Dies funktioniert nur für die konkrete Typvererbung ... Testfall: bool expected = true; bool actual = Program.IsSubclassOfRawGeneric (typeof (IEnumerable <>), typeof (List <string>)); Assert.AreEqual (erwartet, tatsächlich); // schlägt fehl
Bobby
90

(Neu veröffentlicht aufgrund eines massiven Umschreibens)

Die Code-Antwort von JaredPar ist fantastisch, aber ich habe einen Tipp, der es unnötig machen würde, wenn Ihre generischen Typen nicht auf Werttypparametern basieren. Ich war gespannt, warum der "is" -Operator nicht funktionieren würde, deshalb habe ich auch die Ergebnisse meiner Experimente als zukünftige Referenz dokumentiert. Bitte verbessern Sie diese Antwort, um ihre Klarheit weiter zu verbessern.

TRINKGELD:

Wenn Sie sicherstellen, dass Ihre GenericClass-Implementierung von einer abstrakten nicht generischen Basisklasse wie GenericClassBase erbt, können Sie dieselbe Frage ohne Probleme wie folgt stellen:

typeof(Test).IsSubclassOf(typeof(GenericClassBase))

IsSubclassOf ()

Meine Tests haben ergeben, dass IsSubclassOf () bei parameterlosen generischen Typen wie z

typeof(GenericClass<>)

während es mit funktioniert

typeof(GenericClass<SomeType>)

Daher funktioniert der folgende Code für jede Ableitung von GenericClass <>, vorausgesetzt, Sie sind bereit, basierend auf SomeType zu testen:

typeof(Test).IsSubclassOf(typeof(GenericClass<SomeType>))

Ich kann mir nur vorstellen, dass Sie mit GenericClass <> testen möchten, und zwar in einem Plug-in-Framework-Szenario.


Gedanken zum Operator "ist"

Zur Entwurfszeit erlaubt C # die Verwendung parameterloser Generika nicht, da sie zu diesem Zeitpunkt im Wesentlichen kein vollständiger CLR-Typ sind. Daher müssen Sie generische Variablen mit Parametern deklarieren. Aus diesem Grund ist der Operator "is" für die Arbeit mit Objekten so leistungsfähig. Im Übrigen kann der Operator "is" auch keine parameterlosen generischen Typen auswerten.

Der Operator "is" testet die gesamte Vererbungskette einschließlich der Schnittstellen.

Bei einer Instanz eines Objekts reicht die folgende Methode aus:

bool IsTypeof<T>(object t)
{
    return (t is T);
}

Das ist irgendwie überflüssig, aber ich dachte mir, ich würde es für alle visualisieren.

Gegeben

var t = new Test();

Die folgenden Codezeilen würden true zurückgeben:

bool test1 = IsTypeof<GenericInterface<SomeType>>(t);

bool test2 = IsTypeof<GenericClass<SomeType>>(t);

bool test3 = IsTypeof<Test>(t);

Wenn Sie jedoch etwas Spezifisches für GenericClass möchten, können Sie es vermutlich spezifischer gestalten:

bool IsTypeofGenericClass<SomeType>(object t)
{
    return (t is GenericClass<SomeType>);
}

Dann würden Sie so testen:

bool test1 = IsTypeofGenericClass<SomeType>(t);
EnocNRoll - AnandaGopal Pardue
quelle
2
+1 für die Analyse und Prüfung. Auch Ihre Antwort war in meinem Fall sehr nützlich.
Guillermo Gutiérrez
3
Es sollte beachtet werden, dass der Compiler mit .IsSubclassOf (typeof (GenericClass <>)) vollkommen zufrieden ist und einfach nicht das tut, was Sie sich wünschen.
user2880616
2
Aber was ist, wenn SomeType zur Kompilierungszeit nicht bekannt ist?
Ryan The Leach
In der gesamten Diskussion ging es darum, wann Sie nur ein TypeObjekt haben.
Jonathan Wood
@ JonathanWood, deshalb befasst sich meine obige Antwort mit dem typeofOperator. In den Dokumenten heißt es: "Der Operator typeof wird verwendet, um das System.Type-Objekt für einen Typ abzurufen."
EnocNRoll - AnandaGopal Pardue
33

Ich habe einige dieser Proben durchgearbeitet und festgestellt, dass sie in einigen Fällen fehlten. Diese Version funktioniert mit allen Arten von Generika: Typen, Schnittstellen und Typdefinitionen davon.

public static bool InheritsOrImplements(this Type child, Type parent)
{
    parent = ResolveGenericTypeDefinition(parent);

    var currentChild = child.IsGenericType
                           ? child.GetGenericTypeDefinition()
                           : child;

    while (currentChild != typeof (object))
    {
        if (parent == currentChild || HasAnyInterfaces(parent, currentChild))
            return true;

        currentChild = currentChild.BaseType != null
                       && currentChild.BaseType.IsGenericType
                           ? currentChild.BaseType.GetGenericTypeDefinition()
                           : currentChild.BaseType;

        if (currentChild == null)
            return false;
    }
    return false;
}

private static bool HasAnyInterfaces(Type parent, Type child)
{
    return child.GetInterfaces()
        .Any(childInterface =>
        {
            var currentInterface = childInterface.IsGenericType
                ? childInterface.GetGenericTypeDefinition()
                : childInterface;

            return currentInterface == parent;
        });
}

private static Type ResolveGenericTypeDefinition(Type parent)
{
    var shouldUseGenericType = true;
    if (parent.IsGenericType && parent.GetGenericTypeDefinition() != parent)
        shouldUseGenericType = false;

    if (parent.IsGenericType && shouldUseGenericType)
        parent = parent.GetGenericTypeDefinition();
    return parent;
}

Hier sind auch die Unit-Tests:

protected interface IFooInterface
{
}

protected interface IGenericFooInterface<T>
{
}

protected class FooBase
{
}

protected class FooImplementor
    : FooBase, IFooInterface
{
}

protected class GenericFooBase
    : FooImplementor, IGenericFooInterface<object>
{

}

protected class GenericFooImplementor<T>
    : FooImplementor, IGenericFooInterface<T>
{
}


[Test]
public void Should_inherit_or_implement_non_generic_interface()
{
    Assert.That(typeof(FooImplementor)
        .InheritsOrImplements(typeof(IFooInterface)), Is.True);
}

[Test]
public void Should_inherit_or_implement_generic_interface()
{
    Assert.That(typeof(GenericFooBase)
        .InheritsOrImplements(typeof(IGenericFooInterface<>)), Is.True);
}

[Test]
public void Should_inherit_or_implement_generic_interface_by_generic_subclass()
{
    Assert.That(typeof(GenericFooImplementor<>)
        .InheritsOrImplements(typeof(IGenericFooInterface<>)), Is.True);
}

[Test]
public void Should_inherit_or_implement_generic_interface_by_generic_subclass_not_caring_about_generic_type_parameter()
{
    Assert.That(new GenericFooImplementor<string>().GetType()
        .InheritsOrImplements(typeof(IGenericFooInterface<>)), Is.True);
}

[Test]
public void Should_not_inherit_or_implement_generic_interface_by_generic_subclass_not_caring_about_generic_type_parameter()
{
    Assert.That(new GenericFooImplementor<string>().GetType()
        .InheritsOrImplements(typeof(IGenericFooInterface<int>)), Is.False);
}

[Test]
public void Should_inherit_or_implement_non_generic_class()
{
    Assert.That(typeof(FooImplementor)
        .InheritsOrImplements(typeof(FooBase)), Is.True);
}

[Test]
public void Should_inherit_or_implement_any_base_type()
{
    Assert.That(typeof(GenericFooImplementor<>)
        .InheritsOrImplements(typeof(FooBase)), Is.True);
}
fir3rpho3nixx
quelle
2
Ich bin verwirrt über die ResolveGenericTypeDefinition-Methode. Der Variablen "shouldUseGenericType" wird wirklich der Wert zugewiesen: !parent.IsGenericType || parent.GetGenericTypeDefinition() == parent; Sie ersetzen diese Variable also durch die Erweiterung der if-Anweisung: if (parent.IsGenericType && shouldUseGenericType) und Sie erhalten if (parent.IsGenericType && (!parent.IsGenericType || parent.GetGenericTypeDefinition() == parent)) diese , die sich dann aufif (parent.IsGenericType && parent.GetGenericTypeDefinition() == parent)) parent = parent.GetGenericTypeDefinition();
Michael Blackburn
2
Welches scheint nichts zu tun. Wenn dies Werttypen wären, die denen ähneln, int j = 0; if (j is an int && j == 0) { j=0; } bekomme ich meine Referenz gleich und Wert gleich verwechselt? Ich dachte, es gibt nur eine Instanz jedes Typs im Speicher, also zeigen zwei Variablen, die auf denselben Typ verweisen, tatsächlich auf denselben Speicherort im Speicher.
Michael Blackburn
1
@MichaelBlackburn genau richtig :) Ich habe dies so umgestaltet, dass es nur so ist: return parent.IsGenericType? parent.GetGenericTypeDefinition (): parent;
AaronHS
3
Wenn es bereits mit dem Elternteil identisch ist, geben Sie es einfach zurück! und er ruft GetGenericTypeDef zu oft auf. Es muss nur einmal aufgerufen werden
AaronHS
1
Entschuldigung, ich werde unten einen neuen Kommentar hinzufügen.
Menno Deij - van Rijswijk
26

Es scheint mir, dass diese Implementierung in mehreren Fällen funktioniert (generische Klasse und Schnittstelle mit oder ohne initiierte Parameter, unabhängig von der Anzahl der untergeordneten Parameter und Parameter):

public static class ReflexionExtension
{
    public static bool IsSubClassOfGeneric(this Type child, Type parent)
    {
        if (child == parent)
            return false;

        if (child.IsSubclassOf(parent))
            return true;

        var parameters = parent.GetGenericArguments();
        var isParameterLessGeneric = !(parameters != null && parameters.Length > 0 &&
            ((parameters[0].Attributes & TypeAttributes.BeforeFieldInit) == TypeAttributes.BeforeFieldInit));

        while (child != null && child != typeof(object))
        {
            var cur = GetFullTypeDefinition(child);
            if (parent == cur || (isParameterLessGeneric && cur.GetInterfaces().Select(i => GetFullTypeDefinition(i)).Contains(GetFullTypeDefinition(parent))))
                return true;
            else if (!isParameterLessGeneric)
                if (GetFullTypeDefinition(parent) == cur && !cur.IsInterface)
                {
                    if (VerifyGenericArguments(GetFullTypeDefinition(parent), cur))
                        if (VerifyGenericArguments(parent, child))
                            return true;
                }
                else
                    foreach (var item in child.GetInterfaces().Where(i => GetFullTypeDefinition(parent) == GetFullTypeDefinition(i)))
                        if (VerifyGenericArguments(parent, item))
                            return true;

            child = child.BaseType;
        }

        return false;
    }

    private static Type GetFullTypeDefinition(Type type)
    {
        return type.IsGenericType ? type.GetGenericTypeDefinition() : type;
    }

    private static bool VerifyGenericArguments(Type parent, Type child)
    {
        Type[] childArguments = child.GetGenericArguments();
        Type[] parentArguments = parent.GetGenericArguments();
        if (childArguments.Length == parentArguments.Length)
            for (int i = 0; i < childArguments.Length; i++)
                if (childArguments[i].Assembly != parentArguments[i].Assembly || childArguments[i].Name != parentArguments[i].Name || childArguments[i].Namespace != parentArguments[i].Namespace)
                    if (!childArguments[i].IsSubclassOf(parentArguments[i]))
                        return false;

        return true;
    }
}

Hier sind meine 70 76 Testfälle:

[TestMethod]
public void IsSubClassOfGenericTest()
{
    Assert.IsTrue(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(BaseGeneric<>)), " 1");
    Assert.IsFalse(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(WrongBaseGeneric<>)), " 2");
    Assert.IsTrue(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), " 3");
    Assert.IsFalse(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(IWrongBaseGeneric<>)), " 4");
    Assert.IsTrue(typeof(IChildGeneric).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), " 5");
    Assert.IsFalse(typeof(IWrongBaseGeneric<>).IsSubClassOfGeneric(typeof(ChildGeneric2<>)), " 6");
    Assert.IsTrue(typeof(ChildGeneric2<>).IsSubClassOfGeneric(typeof(BaseGeneric<>)), " 7");
    Assert.IsTrue(typeof(ChildGeneric2<Class1>).IsSubClassOfGeneric(typeof(BaseGeneric<>)), " 8");
    Assert.IsTrue(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(BaseGeneric<Class1>)), " 9");
    Assert.IsFalse(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(WrongBaseGeneric<Class1>)), "10");
    Assert.IsTrue(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(IBaseGeneric<Class1>)), "11");
    Assert.IsFalse(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(IWrongBaseGeneric<Class1>)), "12");
    Assert.IsTrue(typeof(IChildGeneric).IsSubClassOfGeneric(typeof(IBaseGeneric<Class1>)), "13");
    Assert.IsFalse(typeof(BaseGeneric<Class1>).IsSubClassOfGeneric(typeof(ChildGeneric2<Class1>)), "14");
    Assert.IsTrue(typeof(ChildGeneric2<Class1>).IsSubClassOfGeneric(typeof(BaseGeneric<Class1>)), "15");
    Assert.IsFalse(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(ChildGeneric)), "16");
    Assert.IsFalse(typeof(IChildGeneric).IsSubClassOfGeneric(typeof(IChildGeneric)), "17");
    Assert.IsFalse(typeof(IBaseGeneric<>).IsSubClassOfGeneric(typeof(IChildGeneric2<>)), "18");
    Assert.IsTrue(typeof(IChildGeneric2<>).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), "19");
    Assert.IsTrue(typeof(IChildGeneric2<Class1>).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), "20");
    Assert.IsFalse(typeof(IBaseGeneric<Class1>).IsSubClassOfGeneric(typeof(IChildGeneric2<Class1>)), "21");
    Assert.IsTrue(typeof(IChildGeneric2<Class1>).IsSubClassOfGeneric(typeof(IBaseGeneric<Class1>)), "22");
    Assert.IsFalse(typeof(IBaseGeneric<Class1>).IsSubClassOfGeneric(typeof(BaseGeneric<Class1>)), "23");
    Assert.IsTrue(typeof(BaseGeneric<Class1>).IsSubClassOfGeneric(typeof(IBaseGeneric<Class1>)), "24");
    Assert.IsFalse(typeof(IBaseGeneric<>).IsSubClassOfGeneric(typeof(BaseGeneric<>)), "25");
    Assert.IsTrue(typeof(BaseGeneric<>).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), "26");
    Assert.IsTrue(typeof(BaseGeneric<Class1>).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), "27");
    Assert.IsFalse(typeof(IBaseGeneric<Class1>).IsSubClassOfGeneric(typeof(IBaseGeneric<Class1>)), "28");
    Assert.IsTrue(typeof(BaseGeneric2<Class1>).IsSubClassOfGeneric(typeof(IBaseGeneric<Class1>)), "29");
    Assert.IsFalse(typeof(IBaseGeneric<>).IsSubClassOfGeneric(typeof(BaseGeneric2<>)), "30");
    Assert.IsTrue(typeof(BaseGeneric2<>).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), "31");
    Assert.IsTrue(typeof(BaseGeneric2<Class1>).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), "32");
    Assert.IsTrue(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(BaseGenericA<,>)), "33");
    Assert.IsFalse(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(WrongBaseGenericA<,>)), "34");
    Assert.IsTrue(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "35");
    Assert.IsFalse(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(IWrongBaseGenericA<,>)), "36");
    Assert.IsTrue(typeof(IChildGenericA).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "37");
    Assert.IsFalse(typeof(IWrongBaseGenericA<,>).IsSubClassOfGeneric(typeof(ChildGenericA2<,>)), "38");
    Assert.IsTrue(typeof(ChildGenericA2<,>).IsSubClassOfGeneric(typeof(BaseGenericA<,>)), "39");
    Assert.IsTrue(typeof(ChildGenericA2<ClassA, ClassB>).IsSubClassOfGeneric(typeof(BaseGenericA<,>)), "40");
    Assert.IsTrue(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(BaseGenericA<ClassA, ClassB>)), "41");
    Assert.IsFalse(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(WrongBaseGenericA<ClassA, ClassB>)), "42");
    Assert.IsTrue(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(IBaseGenericA<ClassA, ClassB>)), "43");
    Assert.IsFalse(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(IWrongBaseGenericA<ClassA, ClassB>)), "44");
    Assert.IsTrue(typeof(IChildGenericA).IsSubClassOfGeneric(typeof(IBaseGenericA<ClassA, ClassB>)), "45");
    Assert.IsFalse(typeof(BaseGenericA<ClassA, ClassB>).IsSubClassOfGeneric(typeof(ChildGenericA2<ClassA, ClassB>)), "46");
    Assert.IsTrue(typeof(ChildGenericA2<ClassA, ClassB>).IsSubClassOfGeneric(typeof(BaseGenericA<ClassA, ClassB>)), "47");
    Assert.IsFalse(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(ChildGenericA)), "48");
    Assert.IsFalse(typeof(IChildGenericA).IsSubClassOfGeneric(typeof(IChildGenericA)), "49");
    Assert.IsFalse(typeof(IBaseGenericA<,>).IsSubClassOfGeneric(typeof(IChildGenericA2<,>)), "50");
    Assert.IsTrue(typeof(IChildGenericA2<,>).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "51");
    Assert.IsTrue(typeof(IChildGenericA2<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "52");
    Assert.IsFalse(typeof(IBaseGenericA<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IChildGenericA2<ClassA, ClassB>)), "53");
    Assert.IsTrue(typeof(IChildGenericA2<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IBaseGenericA<ClassA, ClassB>)), "54");
    Assert.IsFalse(typeof(IBaseGenericA<ClassA, ClassB>).IsSubClassOfGeneric(typeof(BaseGenericA<ClassA, ClassB>)), "55");
    Assert.IsTrue(typeof(BaseGenericA<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IBaseGenericA<ClassA, ClassB>)), "56");
    Assert.IsFalse(typeof(IBaseGenericA<,>).IsSubClassOfGeneric(typeof(BaseGenericA<,>)), "57");
    Assert.IsTrue(typeof(BaseGenericA<,>).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "58");
    Assert.IsTrue(typeof(BaseGenericA<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "59");
    Assert.IsFalse(typeof(IBaseGenericA<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IBaseGenericA<ClassA, ClassB>)), "60");
    Assert.IsTrue(typeof(BaseGenericA2<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IBaseGenericA<ClassA, ClassB>)), "61");
    Assert.IsFalse(typeof(IBaseGenericA<,>).IsSubClassOfGeneric(typeof(BaseGenericA2<,>)), "62");
    Assert.IsTrue(typeof(BaseGenericA2<,>).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "63");
    Assert.IsTrue(typeof(BaseGenericA2<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "64");
    Assert.IsFalse(typeof(BaseGenericA2<ClassB, ClassA>).IsSubClassOfGeneric(typeof(IBaseGenericA<ClassA, ClassB>)), "65");
    Assert.IsFalse(typeof(BaseGenericA<ClassB, ClassA>).IsSubClassOfGeneric(typeof(ChildGenericA2<ClassA, ClassB>)), "66");
    Assert.IsFalse(typeof(BaseGenericA2<ClassB, ClassA>).IsSubClassOfGeneric(typeof(BaseGenericA<ClassA, ClassB>)), "67");
    Assert.IsTrue(typeof(ChildGenericA3<ClassA, ClassB>).IsSubClassOfGeneric(typeof(BaseGenericB<ClassA, ClassB, ClassC>)), "68");
    Assert.IsTrue(typeof(ChildGenericA4<ClassA, ClassB>).IsSubClassOfGeneric(typeof(IBaseGenericB<ClassA, ClassB, ClassC>)), "69");
    Assert.IsFalse(typeof(ChildGenericA3<ClassB, ClassA>).IsSubClassOfGeneric(typeof(BaseGenericB<ClassA, ClassB, ClassC>)), "68-2");
    Assert.IsTrue(typeof(ChildGenericA3<ClassA, ClassB2>).IsSubClassOfGeneric(typeof(BaseGenericB<ClassA, ClassB, ClassC>)), "68-3");
    Assert.IsFalse(typeof(ChildGenericA3<ClassB2, ClassA>).IsSubClassOfGeneric(typeof(BaseGenericB<ClassA, ClassB, ClassC>)), "68-4");
    Assert.IsFalse(typeof(ChildGenericA4<ClassB, ClassA>).IsSubClassOfGeneric(typeof(IBaseGenericB<ClassA, ClassB, ClassC>)), "69-2");
    Assert.IsTrue(typeof(ChildGenericA4<ClassA, ClassB2>).IsSubClassOfGeneric(typeof(IBaseGenericB<ClassA, ClassB, ClassC>)), "69-3");
    Assert.IsFalse(typeof(ChildGenericA4<ClassB2, ClassA>).IsSubClassOfGeneric(typeof(IBaseGenericB<ClassA, ClassB, ClassC>)), "69-4");
    Assert.IsFalse(typeof(bool).IsSubClassOfGeneric(typeof(IBaseGenericB<ClassA, ClassB, ClassC>)), "70");
}

Klassen und Schnittstellen zum Testen:

public class Class1 { }
public class BaseGeneric<T> : IBaseGeneric<T> { }
public class BaseGeneric2<T> : IBaseGeneric<T>, IInterfaceBidon { }
public interface IBaseGeneric<T> { }
public class ChildGeneric : BaseGeneric<Class1> { }
public interface IChildGeneric : IBaseGeneric<Class1> { }
public class ChildGeneric2<Class1> : BaseGeneric<Class1> { }
public interface IChildGeneric2<Class1> : IBaseGeneric<Class1> { }

public class WrongBaseGeneric<T> { }
public interface IWrongBaseGeneric<T> { }

public interface IInterfaceBidon { }

public class ClassA { }
public class ClassB { }
public class ClassC { }
public class ClassB2 : ClassB { }
public class BaseGenericA<T, U> : IBaseGenericA<T, U> { }
public class BaseGenericB<T, U, V> { }
public interface IBaseGenericB<ClassA, ClassB, ClassC> { }
public class BaseGenericA2<T, U> : IBaseGenericA<T, U>, IInterfaceBidonA { }
public interface IBaseGenericA<T, U> { }
public class ChildGenericA : BaseGenericA<ClassA, ClassB> { }
public interface IChildGenericA : IBaseGenericA<ClassA, ClassB> { }
public class ChildGenericA2<ClassA, ClassB> : BaseGenericA<ClassA, ClassB> { }
public class ChildGenericA3<ClassA, ClassB> : BaseGenericB<ClassA, ClassB, ClassC> { }
public class ChildGenericA4<ClassA, ClassB> : IBaseGenericB<ClassA, ClassB, ClassC> { }
public interface IChildGenericA2<ClassA, ClassB> : IBaseGenericA<ClassA, ClassB> { }

public class WrongBaseGenericA<T, U> { }
public interface IWrongBaseGenericA<T, U> { }

public interface IInterfaceBidonA { }
Xav987
quelle
4
Dies ist die einzige Lösung, die für mich funktioniert hat. Ich konnte keine anderen Lösungen finden, die mit Klassen mit mehreren Typparametern funktionieren. Danke dir.
Connor Clark
1
Ich habe es sehr geschätzt, dass Sie all diese Testfälle veröffentlicht haben. Ich denke, dass die Fälle 68 und 69 falsch statt wahr sein sollten, da Sie Klasse B, Klasse A links und Klasse A, Klasse B rechts haben.
Grax32
Du hast recht, @Grax. Ich habe jetzt keine Zeit, die Korrektur vorzunehmen, aber ich werde meinen Beitrag aktualisieren, sobald er fertig ist. Ich denke, die Korrektur muss in der Methode "VerifyGenericArguments" erfolgen
Xav987
1
@Grax: Ich habe etwas Zeit gefunden, um die Korrektur vorzunehmen. Ich habe die ClassB2-Klasse hinzugefügt, VerifyGenericArguments geändert und beim Aufruf von VerifyGenericArguments ein Steuerelement hinzugefügt. Ich habe auch die Fälle 68 und 69 geändert und 68-2, 68-3, 68-4, 69-2, 69-3 und 69-4 hinzugefügt.
Xav987
1
Danke mein Herr. +1 für funktionierende Lösung UND die enorme Anzahl von Testfällen (zumindest für mich enorm).
Eldoïr
10

Der Code von JaredPar funktioniert jedoch nur für eine Vererbungsstufe. Verwenden Sie für unbegrenzte Vererbungsstufen den folgenden Code

public bool IsTypeDerivedFromGenericType(Type typeToCheck, Type genericType)
{
    if (typeToCheck == typeof(object))
    {
        return false;
    }
    else if (typeToCheck == null)
    {
        return false;
    }
    else if (typeToCheck.IsGenericType && typeToCheck.GetGenericTypeDefinition() == genericType)
    {
        return true;
    }
    else
    {
        return IsTypeDerivedFromGenericType(typeToCheck.BaseType, genericType);
    }
}
user53564
quelle
4
Der whileCode in JaredPar deckt unbegrenzte Ebenen ab.
Jay
@ Jay ... und vermeidet Rekursion.
Marc L.
1
@MarcL. Dies verwendet die Schwanzrekursion, daher sollte es für den Compiler trivial sein, die Rekursion zu optimieren.
Darhuuk
10

Hier ist eine kleine Methode, die ich erstellt habe, um zu überprüfen, ob ein Objekt von einem bestimmten Typ abgeleitet ist. Funktioniert super für mich!

internal static bool IsDerivativeOf(this Type t, Type typeToCompare)
{
    if (t == null) throw new NullReferenceException();
    if (t.BaseType == null) return false;

    if (t.BaseType == typeToCompare) return true;
    else return t.BaseType.IsDerivativeOf(typeToCompare);
}

quelle
7

Es könnte übertrieben sein, aber ich verwende Erweiterungsmethoden wie die folgenden. Sie prüfen Schnittstellen sowie Unterklassen. Es kann auch den Typ zurückgeben, der die angegebene generische Definition hat.

Zum Beispiel für das Beispiel in der Frage kann es sowohl gegen die generische Schnittstelle als auch gegen die generische Klasse getestet werden. Der zurückgegebene Typ kann verwendet werden, um GetGenericArgumentszu bestimmen, dass der generische Argumenttyp "SomeType" ist.

/// <summary>
/// Checks whether this type has the specified definition in its ancestry.
/// </summary>   
public static bool HasGenericDefinition(this Type type, Type definition)
{
    return GetTypeWithGenericDefinition(type, definition) != null;
}

/// <summary>
/// Returns the actual type implementing the specified definition from the
/// ancestry of the type, if available. Else, null.
/// </summary>
public static Type GetTypeWithGenericDefinition(this Type type, Type definition)
{
    if (type == null)
        throw new ArgumentNullException("type");
    if (definition == null)
        throw new ArgumentNullException("definition");
    if (!definition.IsGenericTypeDefinition)
        throw new ArgumentException(
            "The definition needs to be a GenericTypeDefinition", "definition");

    if (definition.IsInterface)
        foreach (var interfaceType in type.GetInterfaces())
            if (interfaceType.IsGenericType
                && interfaceType.GetGenericTypeDefinition() == definition)
                return interfaceType;

    for (Type t = type; t != null; t = t.BaseType)
        if (t.IsGenericType && t.GetGenericTypeDefinition() == definition)
            return t;

    return null;
}
Codybartfast
quelle
Guter Post! Schön, die Bedenken in zwei Methoden aufzuteilen.
Wiebe Tijsma
4

Aufbauend auf der hervorragenden Antwort von fir3rpho3nixx und David Schmitt habe ich ihren Code geändert und den ShouldInheritOrImplementTypedGenericInterface-Test (letzter) hinzugefügt.

    /// <summary>
    /// Find out if a child type implements or inherits from the parent type.
    /// The parent type can be an interface or a concrete class, generic or non-generic.
    /// </summary>
    /// <param name="child"></param>
    /// <param name="parent"></param>
    /// <returns></returns>
    public static bool InheritsOrImplements(this Type child, Type parent)
    {
        var currentChild = parent.IsGenericTypeDefinition && child.IsGenericType ? child.GetGenericTypeDefinition() : child;

        while (currentChild != typeof(object))
        {
            if (parent == currentChild || HasAnyInterfaces(parent, currentChild))
                return true;

            currentChild = currentChild.BaseType != null && parent.IsGenericTypeDefinition && currentChild.BaseType.IsGenericType
                                ? currentChild.BaseType.GetGenericTypeDefinition()
                                : currentChild.BaseType;

            if (currentChild == null)
                return false;
        }
        return false;
    }

    private static bool HasAnyInterfaces(Type parent, Type child)
    {
        return child.GetInterfaces().Any(childInterface =>
            {
                var currentInterface = parent.IsGenericTypeDefinition && childInterface.IsGenericType
                    ? childInterface.GetGenericTypeDefinition()
                    : childInterface;

                return currentInterface == parent;
            });

    }

    [Test]
    public void ShouldInheritOrImplementNonGenericInterface()
    {
        Assert.That(typeof(FooImplementor)
            .InheritsOrImplements(typeof(IFooInterface)), Is.True);
    }

    [Test]
    public void ShouldInheritOrImplementGenericInterface()
    {
        Assert.That(typeof(GenericFooBase)
            .InheritsOrImplements(typeof(IGenericFooInterface<>)), Is.True);
    }

    [Test]
    public void ShouldInheritOrImplementGenericInterfaceByGenericSubclass()
    {
        Assert.That(typeof(GenericFooImplementor<>)
            .InheritsOrImplements(typeof(IGenericFooInterface<>)), Is.True);
    }

    [Test]
    public void ShouldInheritOrImplementGenericInterfaceByGenericSubclassNotCaringAboutGenericTypeParameter()
    {
        Assert.That(new GenericFooImplementor<string>().GetType()
            .InheritsOrImplements(typeof(IGenericFooInterface<>)), Is.True);
    }

    [Test]
    public void ShouldNotInheritOrImplementGenericInterfaceByGenericSubclassNotCaringAboutGenericTypeParameter()
    {
        Assert.That(new GenericFooImplementor<string>().GetType()
            .InheritsOrImplements(typeof(IGenericFooInterface<int>)), Is.False);
    }

    [Test]
    public void ShouldInheritOrImplementNonGenericClass()
    {
        Assert.That(typeof(FooImplementor)
            .InheritsOrImplements(typeof(FooBase)), Is.True);
    }

    [Test]
    public void ShouldInheritOrImplementAnyBaseType()
    {
        Assert.That(typeof(GenericFooImplementor<>)
            .InheritsOrImplements(typeof(FooBase)), Is.True);
    }

    [Test]
    public void ShouldInheritOrImplementTypedGenericInterface()
    {
        GenericFooImplementor<int> obj = new GenericFooImplementor<int>();
        Type t = obj.GetType();

        Assert.IsTrue(t.InheritsOrImplements(typeof(IGenericFooInterface<int>)));
        Assert.IsFalse(t.InheritsOrImplements(typeof(IGenericFooInterface<String>)));
    } 
Menno Deij - van Rijswijk
quelle
4

Dies kann alles einfach mit linq gemacht werden. Dadurch werden alle Typen gefunden, die eine Unterklasse der generischen Basisklasse GenericBaseType sind.

    IEnumerable<Type> allTypes = Assembly.GetExecutingAssembly().GetTypes();

    IEnumerable<Type> mySubclasses = allTypes.Where(t => t.BaseType != null 
                                                            && t.BaseType.IsGenericType
                                                            && t.BaseType.GetGenericTypeDefinition() == typeof(GenericBaseType<,>));
DVK
quelle
Dies war die einzige Lösung, die für mich funktioniert hat. Einfach und elegant. Danke dir.
Seidenfeuer
4

Einfache Lösung: Erstellen Sie einfach eine zweite, nicht generische Schnittstelle und fügen Sie sie der generischen Klasse hinzu:

public interface IGenericClass
{
}

public class GenericClass<T> : GenericInterface<T>, IGenericClass
{
}

Dann sind Sie nur für die in irgendeiner Weise Sie gerne mit is, as, IsAssignableFrometc.

if (thing is IGenericClass)
{
    // Do work
{

Natürlich nur möglich, wenn Sie die Möglichkeit haben, die generische Klasse zu bearbeiten (die das OP zu haben scheint), aber es ist etwas eleganter und lesbarer als die Verwendung einer kryptischen Erweiterungsmethode.

kad81
quelle
1
Wenn Sie jedoch nur prüfen, ob etwas vom Typ ist, IGenericClasskönnen Sie dies nicht garantieren GenericClassoder GenericInterfacees wird tatsächlich erweitert oder implementiert. Dies bedeutet, dass Sie mit Ihrem Compiler auch nicht auf Mitglieder der generischen Klasse zugreifen können.
B12Toaster
4

Zur Antwort von @ jaredpar hinzugefügt, verwende ich Folgendes, um nach Schnittstellen zu suchen:

public static bool IsImplementerOfRawGeneric(this Type type, Type toCheck)
{
    if (toCheck.GetTypeInfo().IsClass)
    {
        return false;
    }

    return type.GetInterfaces().Any(interfaceType =>
    {
        var current = interfaceType.GetTypeInfo().IsGenericType ?
                    interfaceType.GetGenericTypeDefinition() : interfaceType;
        return current == toCheck;
    });
}

public static bool IsSubTypeOfRawGeneric(this Type type, Type toCheck)
{
    return type.IsInterface ?
          IsImplementerOfRawGeneric(type, toCheck)
        : IsSubclassOfRawGeneric(type, toCheck);
}

Ex:

Console.WriteLine(typeof(IList<>).IsSubTypeOfRawGeneric(typeof(IList<int>))); // true
ärgerlich
quelle
Gute Ergänzung. Hat euch und Jaredpar eine Gegenstimme gegeben. Mein einziger Kommentar ist, dass Sie die Position der Typen (aus Jaredpars Antwort) aufgrund der Erweiterungsmethode umgekehrt haben. Ich habe es als Erweiterungsmethode entfernt und es hat mich ein bisschen abgeworfen. Nicht dein Problem, sondern meins. Ich wollte nur der nächsten Person den Kopf hoch geben. Danke noch einmal.
Tony
@ Tony, danke für den Tipp, aber beim nächsten Mal Aktualisiere die Antwort.
Johnny 5.
@vexe, Testen ist wichtig. Ihre ursprüngliche Antwort ist fehlerhaft. Sie funktioniert nur, weil Sie sie auf einer Schnittstelle getestet haben. Zweitens verschwenden wir Rechenleistung, indem wir diese Funktion unabhängig vom Typ ausführen.
Johnny 5.
2

JaredPar,

Dies hat bei mir nicht funktioniert, wenn ich typeof (type <>) als toCheck übergeben habe. Folgendes habe ich geändert.

static bool IsSubclassOfRawGeneric(Type generic, Type toCheck) {
    while (toCheck != typeof(object)) {
        var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
          if (cur.IsGenericType && generic.GetGenericTypeDefinition() == cur.GetGenericTypeDefinition()) {
            return true;
        }
        toCheck = toCheck.BaseType;
    }
    return false;
}
Cirem
quelle
Die Lösung von JaredPar funktioniert tatsächlich typeof(type<>)als toCheck. Außerdem benötigen Sie wirklich die Nullprüfung wie in der JaredPar-Lösung. Darüber hinaus weiß ich nicht, was Sie sonst noch erreichen, wenn Sie cur == genericseine Lösung durch eine cur.IsGenericType && generic.GetGenericTypeDefinition() == cur.GetGenericTypeDefinition()andere ersetzen, als Ihre Methode so einzuschränken, dass sie nur für generische Typen funktioniert. Mit anderen Worten, so etwas scheitert mit einer Ausnahme:IsSubclassOfRawGeneric(typeof(MyClass), typeof(MyClass<>))
Nawfal
2

@EnocNRoll - Die Antwort von Ananda Gopal ist interessant, aber falls eine Instanz nicht vorher instanziiert wurde oder Sie mit einer generischen Typdefinition überprüfen möchten, würde ich diese Methode vorschlagen:

public static bool TypeIs(this Type x, Type d) {
    if(null==d) {
        return false;
    }

    for(var c = x; null!=c; c=c.BaseType) {
        var a = c.GetInterfaces();

        for(var i = a.Length; i-->=0;) {
            var t = i<0 ? c : a[i];

            if(t==d||t.IsGenericType&&t.GetGenericTypeDefinition()==d) {
                return true;
            }
        }
    }

    return false;
}

und benutze es wie:

var b = typeof(char[]).TypeIs(typeof(IList<>)); // true

Es gibt vier bedingte Fälle , in denen beide t(zu prüfendem) und dsind generische Typen und zwei Fälle werden durch abgedeckt t==ddenen (1) weder tnoch dist eine allgemeine Definition oder (2) beide von ihnen sind allgemeine Definitionen . Die übrigen Fälle sind einer von ihnen ist eine generische Definition. Nur wenn des sich bereits um eine generische Definition handelt, haben wir die Möglichkeit zu sagen, dass a eine tist,d aber nicht umgekehrt.

Es sollte mit beliebigen Klassen oder Schnittstellen funktionieren, die Sie testen möchten, und gibt zurück, als ob Sie eine Instanz dieses Typs mit dem isOperator testen würden .

Ken Kin
quelle
0
Type _type = myclass.GetType();
PropertyInfo[] _propertyInfos = _type.GetProperties();
Boolean _test = _propertyInfos[0].PropertyType.GetGenericTypeDefinition() 
== typeof(List<>);

quelle
0

Sie können diese Erweiterung ausprobieren

    public static bool IsSubClassOfGenericClass(this Type type, Type genericClass,Type t)
    {
        return type.IsSubclassOf(genericClass.MakeGenericType(new[] { t }));
    }
Yaseen
quelle
0

Spät zum Spiel ... Ich habe auch noch eine weitere Permutation von JarodPars Antwort.

Hier ist Type.IsSubClassOf (Type) mit freundlicher Genehmigung des Reflektors:

    public virtual bool IsSubclassOf(Type c)
    {
        Type baseType = this;
        if (!(baseType == c))
        {
            while (baseType != null)
            {
                if (baseType == c)
                {
                    return true;
                }
                baseType = baseType.BaseType;
            }
            return false;
        }
        return false;
    }

Daraus sehen wir, dass es nichts zu Cray Cray macht und dem iterativen Ansatz von JaredPar ähnlich ist. So weit, ist es gut. Hier ist meine Version (Haftungsausschluss: nicht gründlich getestet, also lass es mich wissen, wenn du Probleme findest)

    public static bool IsExtension(this Type thisType, Type potentialSuperType)
    {
        //
        // protect ya neck
        //
        if (thisType == null || potentialSuperType == null || thisType == potentialSuperType) return false;

        //
        // don't need to traverse inheritance for interface extension, so check/do these first
        //
        if (potentialSuperType.IsInterface)
        {
            foreach (var interfaceType in thisType.GetInterfaces())
            {
                var tempType = interfaceType.IsGenericType ? interfaceType.GetGenericTypeDefinition() : interfaceType;

                if (tempType == potentialSuperType)
                {
                    return true;
                }
            }
        }

        //
        // do the concrete type checks, iterating up the inheritance chain, as in orignal
        //
        while (thisType != null && thisType != typeof(object))
        {
            var cur = thisType.IsGenericType ? thisType.GetGenericTypeDefinition() : thisType;

            if (potentialSuperType == cur)
            {
                return true;
            }

            thisType = thisType.BaseType;
        }
        return false;
    }

Im Grunde ist dies nur eine Erweiterungsmethode für System.Type. Ich habe dies getan, um den Typ "thisType" absichtlich auf konkrete Typen zu beschränken, da ich sofort die LINQ-Abfrage "where" als Prädikate für Typobjekte verwende. Ich bin sicher, alle klugen Leute da draußen könnten es auf eine effiziente statische Allzweckmethode reduzieren, wenn Sie müssen :) Der Code macht ein paar Dinge, die der Code der Antwort nicht tut

  1. Öffnen Sie es bis zur allgemeinen "Erweiterung" - ich erwäge Vererbung (Think-Klassen) sowie Implementierung (Schnittstellen); Methoden- und Parameternamen werden geändert, um dies besser widerzuspiegeln
  2. Eingabe Nullvalidierung (meah)
  3. Eingabe des gleichen Typs (eine Klasse kann sich nicht selbst erweitern)
  4. Kurzschlussausführung, wenn der betreffende Typ eine Schnittstelle ist; Da GetInterfaces () alle implementierten Schnittstellen zurückgibt (auch solche, die in Superklassen implementiert sind), können Sie diese Sammlung einfach durchlaufen, ohne den Vererbungsbaum erklimmen zu müssen

Der Rest ist im Grunde der gleiche wie der Code von JaredPar

isandburn
quelle