Controleer of een klasse is afgeleid van een generieke klasse

Ik heb een generieke klasse in mijn project met afgeleide klassen.

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

Is er een manier om erachter te komen of een Type-object is afgeleid van GenericClass?

t.IsSubclassOf(typeof(GenericClass<>))

werkt niet.


Antwoord 1, autoriteit 100%

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

Antwoord 2, autoriteit 21%

(Opnieuw gepost vanwege een enorme herschrijving)

Het antwoord op de code van JaredPar is fantastisch, maar ik heb een tip die het overbodig zou maken als je generieke typen niet zijn gebaseerd op parameters van het waardetype. Ik werd opgehangen over waarom de “is” -operator niet zou werken, dus ik heb ook de resultaten van mijn experimenten gedocumenteerd voor toekomstig gebruik. Verbeter dit antwoord om de duidelijkheid ervan verder te vergroten.

TIP:

Als u er zeker van bent dat uw GenericClass-implementatie erft van een abstracte niet-generieke basisklasse zoals GenericClassBase, kunt u dezelfde vraag zonder enige moeite als volgt stellen:

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

IsSubclassOf()

Mijn testen geven aan dat IsSubclassOf() niet werkt op parameterloze generieke typen zoals

typeof(GenericClass<>)

terwijl het zal werken met

typeof(GenericClass<SomeType>)

Daarom werkt de volgende code voor elke afleiding van GenericClass<>, ervan uitgaande dat u bereid bent te testen op basis van SomeType:

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

De enige keer dat ik me kan voorstellen dat je zou willen testen door GenericClass<> bevindt zich in een plug-in-frameworkscenario.


Gedachten over de “is”-operator

Tijdens het ontwerp staat C# het gebruik van parameterloze generieke geneesmiddelen niet toe, omdat ze op dat moment in wezen geen volledig CLR-type zijn. Daarom moet u generieke variabelen met parameters declareren en daarom is de “is”-operator zo krachtig voor het werken met objecten. Overigens kan de “is”-operator ook geen parameterloze generieke typen evalueren.

De “is”-operator test de hele overervingsketen, inclusief interfaces.

Dus, gegeven een instantie van een object, zal de volgende methode het lukken:

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

Dit is een beetje overbodig, maar ik dacht ik ga door en visualiseer het voor iedereen.

Gegeven

var t = new Test();

De volgende regels code zouden true retourneren:

bool test1 = IsTypeof<GenericInterface<SomeType>>(t);
bool test2 = IsTypeof<GenericClass<SomeType>>(t);
bool test3 = IsTypeof<Test>(t);

Aan de andere kant, als je iets specifieks voor GenericClass wilt, zou je het specifieker kunnen maken, neem ik aan, als volgt:

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

Dan zou je als volgt testen:

bool test1 = IsTypeofGenericClass<SomeType>(t);

Antwoord 3, autoriteit 7%

Ik heb enkele van deze voorbeelden doorgenomen en ontdekte dat ze in sommige gevallen ontbraken. Deze versie werkt met allerlei generieke geneesmiddelen: typen, interfaces en typedefinities daarvan.

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 zijn ook de 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);
}

Antwoord 4, autoriteit 7%

Het lijkt mij dat deze implementatie in meer gevallen werkt (generieke klasse en interface met of zonder geïnitieerde parameters, ongeacht het aantal onderliggende parameters en parameters):

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 zijn mijn 7076 testcases:

[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 en interfaces voor 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 { }

Antwoord 5

Toegevoegd aan @jaredpar’s antwoord, hier is wat ik gebruik om te controleren op interfaces:

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

Bijvoorbeeld:

Console.WriteLine(typeof(IList<>).IsSubTypeOfRawGeneric(typeof(IList<int>))); // true

Antwoord 6

Eenvoudige oplossing: maak en voeg gewoon een tweede, niet-generieke interface toe aan de generieke klasse:

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

Controleer dat dan op elke gewenste manier met behulp van is, as, IsAssignableFrom, enz.

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

Uiteraard alleen mogelijk als je de mogelijkheid hebt om de generieke klasse te bewerken (die de OP lijkt te hebben), maar het is een beetje eleganter en leesbaarder dan het gebruik van een cryptische uitbreidingsmethode.


Antwoord 7

Voortbouwend op het uitstekende antwoord hierboven van fir3rpho3nixx en David Schmitt, heb ik hun code aangepast en de ShouldInheritOrImplementTypedGenericInterface-test (laatste) toegevoegd.

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

Antwoord 8

JaredPar,

Dit werkte niet voor mij als ik typeof(type<>) doorgeef als toCheck. Dit is wat ik heb veranderd.

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

Antwoord 9

@EnocNRoll – Het antwoord van Ananda Gopal is interessant, maar als een instantie niet vooraf is geïnstantieerd of als je het wilt controleren met een generieke typedefinitie, raad ik deze methode aan:

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

en gebruik het als:

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

Er zijn vier voorwaardelijke gevallen waarin zowel t(te testen) als dgenerieke typen zijn en twee gevallen vallen onder t==ddie (1) noch tnoch dis een generieke definitieof (2) beide zijn generieke definities. De overige gevallen zijn een van hen is een generieke definitie, alleen als dal een generieke definitie is, hebben we de kans om te zeggen dat a teen dmaar niet andersom.

Het zou moeten werken met willekeurige klassen of interfaces die u wilt testen, en geeft als resultaat alsof u een instantie van dat type test met de operator is.


Antwoord 10

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

Antwoord 11

U kunt deze extensie proberen

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

Antwoord 12

laat in het spel hierover… ik heb ook nog een andere permutatie van JarodPar’s antwoord.

hier is Type.IsSubClassOf(Type) met dank aan reflector:

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

Daaruit zien we dat het niets bijzonders doet en vergelijkbaar is met de iteratieve benadering van JaredPar. tot nu toe, zo goed. hier is mijn versie (disclaimer: niet grondig getest, dus laat het weten als je problemen vindt)

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

In feite is dit slechts een uitbreidingsmethode voor System.Type – ik deed dit om het “thisType” Type opzettelijk te beperken tot concrete Types, aangezien ik het onmiddellijk gebruik om LINQ-query’s met “waar” predikaten tegen Type-objecten. ik weet zeker dat alle slimme mensen die er zijn, het kunnen omzetten in een efficiënte, statische methode voor alle doeleinden als dat nodig is 🙂 de code doet een paar dingen die de code van het antwoord niet doet

  1. staat open voor algemene “extensie” – ik overweeg overerving (denk aan klassen)
    evenals implementatie (interfaces); methode- en parameternamen
    zijn gewijzigd om dit beter weer te geven
  2. voer null-validatie in (meah)
  3. invoer van hetzelfde type (een klasse kan zichzelf niet uitbreiden)
  4. kortsluiting uitvoering als Type in kwestie een interface is; omdat GetInterfaces() alle geïmplementeerde interfaces retourneert (zelfs degenen die in superklassen zijn geïmplementeerd), kun je eenvoudig door die verzameling lopen zonder de overervingsboom te hoeven beklimmen

de rest is in principe hetzelfde als de code van JaredPar

Other episodes