Sjekk om en klasse er avledet fra en generisk klasse

 C Programming >> C C# Program >  >> C#
Sjekk om en klasse er avledet fra en generisk klasse

Prøv denne koden

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

(Lagt ut på nytt på grunn av en massiv omskriving)

JaredPars kodesvar er fantastisk, men jeg har et tips som vil gjøre det unødvendig hvis de generiske typene dine ikke er basert på verditypeparametere. Jeg ble hengt opp i hvorfor "er"-operatøren ikke ville fungere, så jeg har også dokumentert resultatene av eksperimentet mitt for fremtidig referanse. Forbedr dette svaret for å gjøre det ytterligere tydeligere.

TIPS:

Hvis du sørger for at GenericClass-implementeringen din arver fra en abstrakt ikke-generisk basisklasse som GenericClassBase, kan du stille det samme spørsmålet uten problemer som dette:

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

IsSubclassOf()

Testingen min indikerer at IsSubclassOf() ikke fungerer på parameterløse generiske typer som

typeof(GenericClass<>)

mens det vil fungere med

typeof(GenericClass<SomeType>)

Derfor vil følgende kode fungere for enhver avledning av GenericClass<>, forutsatt at du er villig til å teste basert på SomeType:

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

Den eneste gangen jeg kan forestille meg at du ønsker å teste med GenericClass<> er i et plug-in rammeverksscenario.

Tanker om "er"-operatøren

På designtidspunktet tillater ikke C# bruk av parameterløse generiske stoffer fordi de i hovedsak ikke er en komplett CLR-type på det tidspunktet. Derfor må du deklarere generiske variabler med parametere, og det er derfor "er"-operatoren er så kraftig for å jobbe med objekter. Forresten, "er"-operatøren kan heller ikke evaluere parameterløse generiske typer.

"Er"-operatøren vil teste hele arvekjeden, inkludert grensesnitt.

Så gitt en forekomst av ethvert objekt, vil følgende metode gjøre susen:

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

Dette er på en måte overflødig, men jeg tenkte at jeg ville gå videre og visualisere det for alle.

Gitt

var t = new Test();

Følgende kodelinjer vil returnere true:

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

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

bool test3 = IsTypeof<Test>(t);

På den annen side, hvis du vil ha noe spesifikt for GenericClass, kan du gjøre det mer spesifikt, antar jeg, slik:

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

Da ville du teste slik:

bool test1 = IsTypeofGenericClass<SomeType>(t);

Jeg jobbet gjennom noen av disse prøvene og fant ut at de manglet i noen tilfeller. Denne versjonen fungerer med alle typer generikk:typer, grensesnitt og typedefinisjoner av disse.

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

Her er også enhetstestene:

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