Controlla se una classe è derivata da una classe generica


309

Ho una classe generica nel mio progetto con classi derivate.

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

public class Test : GenericClass<SomeType>
{
}

C'è un modo per scoprire se un Typeoggetto è derivato GenericClass?

t.IsSubclassOf(typeof(GenericClass<>))

non funziona.

Risposte:


430

Prova questo codice

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

4
Questo è un dolce pezzo di codice, devo dire. L'implementazione del ciclo while evita anche il superamento delle prestazioni di ricorsione non necessarie. È una soluzione elegante e bella a una domanda meta-generica.
EnocNRoll - AnandaGopal Pardue,

2
Ho aggiunto questo metodo alla mia classe statica ReflectionUtils nel mio framework e l'ho anche adattato come metodo di estensione per l'oggetto definendo toCheck all'interno del metodo come Type toCheck = obj.GetType (); dato "questo oggetto obj" è il primo parametro.
EnocNRoll - AnandaGopal Pardue,

11
Il ciclo while non si interromperà se il tipo toCheck non è una classe (ad es. Interfaccia). Ciò causerà una NullReferenceException.
JD Courtoy,

2
Questo restituirà anche vero se toCheck è il tipo generico che stai cercando.
oillio,

14
Questo funziona solo per l'ereditarietà di tipo concreto ... Caso di prova: bool previsti = vero; bool actual = Program.IsSubclassOfRawGeneric (typeof (IEnumerable <>), typeof (List <string>)); Assert.AreEqual (previsto, effettivo); // fallisce
Bobby,

90

(Ripubblicato a causa di un'enorme riscrittura)

La risposta del codice di JaredPar è fantastica, ma ho un suggerimento che lo renderebbe superfluo se i tuoi tipi generici non si basano su parametri del tipo di valore. Sono stato bloccato dal motivo per cui l'operatore "is" non avrebbe funzionato, quindi ho anche documentato i risultati della mia sperimentazione per riferimento futuro. Si prega di migliorare questa risposta per migliorare ulteriormente la sua chiarezza.

MANCIA:

Se si accerta che l'implementazione di GenericClass erediti da una classe di base non generica astratta come GenericClassBase, è possibile porre la stessa domanda senza alcun problema in questo modo:

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

IsSubclassOf ()

Il mio test indica che IsSubclassOf () non funziona su tipi generici senza parametri come

typeof(GenericClass<>)

mentre funzionerà con

typeof(GenericClass<SomeType>)

Pertanto, il codice seguente funzionerà per qualsiasi derivazione di GenericClass <>, supponendo che tu sia disposto a testare in base a SomeType:

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

L'unica volta che posso immaginare che vorresti provare con GenericClass <> è in uno scenario di plug-in framework.


Pensieri sull'operatore "is"

In fase di progettazione, C # non consente l'uso di generici senza parametri poiché in quel momento non sono essenzialmente un tipo CLR completo. Pertanto, è necessario dichiarare variabili generiche con parametri ed è per questo che l'operatore "is" è così potente per lavorare con gli oggetti. Per inciso, anche l'operatore "is" non può valutare tipi generici senza parametri.

L'operatore "is" testerà l'intera catena di ereditarietà, comprese le interfacce.

Quindi, data un'istanza di qualsiasi oggetto, il seguente metodo farà il trucco:

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

Questo è un po 'ridondante, ma ho pensato di andare avanti e visualizzarlo per tutti.

Dato

var t = new Test();

Le seguenti righe di codice restituiscono true:

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

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

bool test3 = IsTypeof<Test>(t);

D'altra parte, se vuoi qualcosa di specifico per GenericClass, potresti renderlo più specifico, suppongo, in questo modo:

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

Quindi testeresti così:

bool test1 = IsTypeofGenericClass<SomeType>(t);

2
+1 per l'analisi e il test. Inoltre, la tua risposta è stata molto utile nel mio caso.
Guillermo Gutiérrez,

3
Va notato che il compilatore è perfettamente soddisfatto di .IsSubclassOf (typeof (GenericClass <>)), semplicemente non fa quello che potresti desiderare.
user2880616

2
E se SomeType non fosse noto al momento della compilazione?
Ryan The Leach,

Il punto dell'intera discussione riguardava quando hai solo un Typeoggetto.
Jonathan Wood,

@JonathanWood, ecco perché la mia risposta sopra riguarda l' typeofoperatore. Secondo i documenti: "L'operatore typeof viene utilizzato per ottenere l'oggetto System.Type per un tipo".
EnocNRoll - AnandaGopal Pardue

33

Ho analizzato alcuni di questi campioni e ho scoperto che in alcuni casi mancavano. Questa versione funziona con tutti i tipi di generici: tipi, interfacce e definizioni dei relativi tipi.

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

Ecco anche i test unitari:

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

2
Sono perplesso sul metodo ResolveGenericTypeDefinition. Alla variabile "shouldUseGenericType" viene realmente assegnato il valore: !parent.IsGenericType || parent.GetGenericTypeDefinition() == parent; Quindi sostituisci quella variabile con l'espansione dell'istruzione if: if (parent.IsGenericType && shouldUseGenericType) e ottieni if (parent.IsGenericType && (!parent.IsGenericType || parent.GetGenericTypeDefinition() == parent)) che si riduce a if (parent.IsGenericType && parent.GetGenericTypeDefinition() == parent)) parent = parent.GetGenericTypeDefinition();
Michael Blackburn il

2
Che sembrerebbe non fare nulla. Se questi fossero tipi di valore che sarebbero simili a int j = 0; if (j is an int && j == 0) { j=0; } Sto ottenendo il mio riferimento uguale e il valore uguale mischiato? Ho pensato che ci fosse solo un'istanza di ciascun tipo in memoria, quindi due variabili che puntano allo stesso tipo puntano infatti alla stessa posizione in memoria.
Michael Blackburn,

1
@MichaelBlackburn spot :) ho refactored questo per essere solo: return parent.IsGenericType? parent.GetGenericTypeDefinition (): parent;
AaronHS,

3
se è già lo stesso del genitore, restituiscilo! e chiama GetGenericTypeDef troppe volte. Deve solo essere chiamato una volta
AaronHS il

1
Scusa, aggiungerò un nuovo commento qui sotto.
Menno Deij - van Rijswijk,

26

Mi sembra che questa implementazione funzioni in più casi (classe generica e interfaccia con o senza parametri avviati, indipendentemente dal numero di figlio e parametri):

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

Ecco i miei 70 76 casi di test:

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

Classi e interfacce per i test:

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

4
Questa è l'unica soluzione che ha funzionato per me. Non sono riuscito a trovare altre soluzioni che funzionassero con classi con più parametri di tipo. Grazie.
Connor Clark,

1
Ho davvero apprezzato la tua pubblicazione di tutti questi casi di test. Penso che i casi 68 e 69 dovrebbero essere falsi invece che veri, dato che hai Classe B, Classe A a sinistra e Classe A, Classe B a destra.
Grax32

Hai ragione, @Grax. Non ho tempo per apportare la correzione ora, ma aggiornerò il mio post non appena sarà fatto. Penso che la correzione debba essere nel metodo "VerifyGenericArguments"
Xav987,

1
@Grax: ho trovato del tempo per apportare la correzione. Ho aggiunto la classe ClassB2, ho cambiato VerifyGenericArguments e ho aggiunto un controllo alla chiamata di VerifyGenericArguments. Ho anche modificato i casi 68 e 69 e aggiunto 68-2, 68-3, 68-4, 69-2, 69-3 e 69-4.
Xav987,

1
Grazie Signore. +1 per la soluzione di lavoro E l'enorme quantità di casi di test (almeno tremendi per me).
Eldoïr,

10

Il codice di JaredPar funziona ma solo per un livello di eredità. Per livelli illimitati di ereditarietà, utilizzare il seguente codice

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

4
Il whilecodice di JaredPar copre livelli illimitati.
Jay,

@jay ... ed evita la ricorsione.
Marc L.

1
@MarcL. Questo utilizza la ricorsione della coda, quindi dovrebbe essere banale per il compilatore ottimizzare la ricorsione in uscita.
Darhuuk,

10

Ecco un piccolo metodo che ho creato per verificare che un oggetto sia derivato da un tipo specifico. Funziona benissimo per me!

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

7

Potrebbe essere eccessivo ma utilizzo metodi di estensione come i seguenti. Controllano le interfacce e le sottoclassi. Può anche restituire il tipo con la definizione generica specificata.

Ad esempio, per l'esempio nella domanda, può testare su un'interfaccia generica e su una classe generica. Il tipo restituito può essere utilizzato con GetGenericArgumentsper determinare che il tipo di argomento generico è "SomeType".

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

Buon post! Bello dividere le preoccupazioni in due modi.
Wiebe Tijsma,

4

Sulla base dell'eccellente risposta di cui sopra fir3rpho3nixx e David Schmitt, ho modificato il loro codice e aggiunto il test ShouldInheritOrImplementTypedGenericInterface (ultimo).

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

4

Tutto ciò può essere fatto facilmente con linq. Questo troverà tutti i tipi che sono una sottoclasse della classe base generica GenericBaseType.

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

    IEnumerable<Type> mySubclasses = allTypes.Where(t => t.BaseType != null 
                                                            && t.BaseType.IsGenericType
                                                            && t.BaseType.GetGenericTypeDefinition() == typeof(GenericBaseType<,>));

Questa è stata l'unica soluzione che ha funzionato per me. Semplice ed elegante. Grazie.
Silkfire,

4

Soluzione semplice: basta creare e aggiungere una seconda interfaccia non generica alla classe generica:

public interface IGenericClass
{
}

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

Poi basta verificare che in qualche modo ti piace usare is, as, IsAssignableFrom, etc.

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

Ovviamente possibile solo se hai la possibilità di modificare la classe generica (che sembra avere l'OP), ma è un po 'più elegante e leggibile rispetto all'utilizzo di un metodo di estensione criptica.


1
tuttavia, solo verificare se qualcosa è di tipo IGenericClassnon ti garantirà che GenericClasso GenericInterfacesiano effettivamente estesi o implementati. Questo significa che anche il tuo compilatore non ti consentirà di accedere ad alcun membro della classe generica.
B12Taster

4

Aggiunto alla risposta di @ jaredpar, ecco cosa uso per verificare le interfacce:

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

Buona aggiunta. Ha dato sia a te che a Jaredpar un voto positivo. Il mio unico commento è che hai invertito la posizione dei tipi (dalla risposta di jaredpar) a causa del metodo di estensione. L'ho rimosso come metodo di estensione e mi ha gettato via per un po '. Non è un tuo problema, ma il mio. Volevo solo dare la testa alla prossima persona. Grazie ancora.
Tony,

@Tony, grazie per il suggerimento, ma la prossima volta aggiorna la risposta.
johnny 5

@vexe, il test è importante perché la tua risposta originale è rotta, funziona solo perché l'hai testata su un'interfaccia. In secondo luogo, stiamo sprecando potenza di elaborazione eseguendo questa funzione indipendentemente dal tipo.
johnny 5

2

JaredPar,

Questo non ha funzionato per me se passo typeof (type <>) come toCheck. Ecco cosa ho cambiato.

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

La soluzione di JaredPar funziona davvero per typeof(type<>)come toCheck. Inoltre hai davvero bisogno del controllo null come nella soluzione di JaredPar. Inoltre, non so cos'altro stai ottenendo sostituendo la cur == genericsua soluzione cur.IsGenericType && generic.GetGenericTypeDefinition() == cur.GetGenericTypeDefinition()se non limitando il tuo metodo a funzionare solo per tipi generici. In altre parole, qualcosa del genere fallisce con un'eccezione:IsSubclassOfRawGeneric(typeof(MyClass), typeof(MyClass<>))
nawfal

2

@EnocNRoll - La risposta di Ananda Gopal è interessante, ma nel caso in cui un'istanza non sia istanziata in anticipo o si desidera verificare con una definizione di tipo generico, suggerirei questo metodo:

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

e usalo come:

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

Esistono quattro casi condizionali quando entrambi t(da testare) e dsono tipi generici e due casi sono coperti dai t==dquali sono (1) tduna definizione generica(2) entrambi sono definizioni generiche . Il resto dei casi è uno di questi è una definizione generica, solo quando dè già una definizione generica abbiamo la possibilità di dire che a tè und ma non viceversa.

Dovrebbe funzionare con classi o interfacce arbitrarie che si desidera testare e restituisce come se si provasse un'istanza di quel tipo con l' isoperatore.


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

0

Puoi provare questa estensione

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

0

tardi al gioco su questo ... anch'io ho ancora un'altra permutazione della risposta di JarodPar.

ecco Type.IsSubClassOf (Type) per gentile concessione di riflettore:

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

da ciò, vediamo che non sta facendo nulla di troppo cray ed è simile all'approccio iterativo di JaredPar. Fin qui tutto bene. ecco la mia versione (dichiarazione di non responsabilità: non completamente testata, quindi fammi sapere se trovi problemi)

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

fondamentalmente questo è solo un metodo di estensione a System.Type - l'ho fatto per limitare intenzionalmente il tipo "thisType" ai tipi concreti, poiché il mio uso immediato è la query LINQ "dove" si basa su oggetti Type. sono sicuro che tutte le persone intelligenti là fuori potrebbero abbatterlo in un metodo statico efficiente e per tutti gli usi se necessario :) il codice fa alcune cose il codice della risposta no

  1. aprirlo fino alla "estensione" generale - sto prendendo in considerazione l'ereditarietà (think class) e l'implementazione (interfacce); i nomi dei metodi e dei parametri vengono modificati per riflettere meglio questo
  2. input null-validation (meah)
  3. input dello stesso tipo (una classe non può estendersi)
  4. esecuzione di cortocircuito se Tipo in questione è un'interfaccia; poiché GetInterfaces () restituisce tutte le interfacce implementate (anche quelle implementate in superclasse), è possibile semplicemente eseguire il ciclo attraverso quella raccolta senza dover scalare l'albero dell'ereditarietà

il resto è sostanzialmente lo stesso del codice di JaredPar

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.