Проверьте, является ли класс производным от универсального класса

В моем проекте есть общий класс с производными классами.

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

public class Test : GenericClass<SomeType>
{
}

Есть ли способ узнать, является ли объект Type производным от GenericClass?

t.IsSubclassOf(typeof(GenericClass<>))

не работает.


person bernhardrusch    schedule 19.01.2009    source источник


Ответы (15)


Попробуйте этот код

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;
}
person JaredPar    schedule 19.01.2009
comment
Должен сказать, это приятный фрагмент кода. Реализация цикла while также позволяет избежать ненужного снижения производительности рекурсии. Это элегантное и красивое решение мета-общего вопроса. - person EnocNRoll - AnandaGopal Pardue; 19.01.2009
comment
Я добавил этот метод в свой статический класс ReflectionUtils в своей структуре, а также адаптировал его как метод расширения для объекта, определив toCheck внутри метода как Type toCheck = obj.GetType (); для данного объекта obj является первым параметром. - person EnocNRoll - AnandaGopal Pardue; 24.01.2009
comment
Цикл while не прервется, если тип toCheck не является классом (т. Е. Интерфейсом). Это вызовет исключение NullReferenceException. - person JD Courtoy; 24.12.2009
comment
не могли бы вы взглянуть на это: stackoverflow.com/questions/2477577/ для VB.NET 2 есть ли что-то подобное? - person serhio; 19.03.2010
comment
Это также вернет true, если toCheck - это универсальный тип, который вы ищете. - person oillio; 05.05.2010
comment
Это работает только для конкретного наследования типов ... Тестовый пример: bool expected = true; bool actual = Program.IsSubclassOfRawGeneric (typeof (IEnumerable ‹›), typeof (List ‹string›)); Assert.AreEqual (ожидаемый, фактический); // терпит неудачу - person Bobby; 13.07.2010
comment
Вероятно, было бы полезно где-то кэшировать вызов typeof (object), чтобы он не вызывался повторно в цикле while. - person rossisdead; 11.11.2011
comment
Как заявил @oillio, это не ведет себя так же, как IsSubclass, он должен включать: if (genericType == toCheck) {return false} в первой строке - person Robert Sirre; 27.02.2012
comment
Я добавил Type [] interfaces = cur.GetInterfaces (); и проверил их также для моей ситуации, если это кому-то поможет - person Jon; 14.09.2012
comment
@JaredPar Хорошее решение. В зависимости от случая может быть доступен только IsConstructedGenericType, и код необходимо будет адаптировать. - person Lorenz Lo Sauer; 25.07.2016
comment
Для netcore используйте toCheck.GetTypeInfo().IsGenericType и toCheck.GetTypeInfo().BaseType вместо toCheck.IsGenericType и toCheck.BaseType - person amd; 06.05.2017

(Перепубликовано из-за масштабной перезаписи)

Ответ кода JaredPar фантастический, но у меня есть совет, который сделает его ненужным, если ваши общие типы не основаны на параметрах типа значения. Я был зациклен на том, почему оператор «is» не работает, поэтому я также задокументировал результаты своих экспериментов для использования в будущем. Пожалуйста, улучшите этот ответ, чтобы еще больше повысить его ясность.

КОНЧИК:

Если вы уверены, что ваша реализация GenericClass наследуется от абстрактного неуниверсального базового класса, такого как GenericClassBase, вы можете без проблем задать тот же вопрос, например:

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

IsSubclassOf ()

Мое тестирование показывает, что IsSubclassOf () не работает с универсальными типами без параметров, такими как

typeof(GenericClass<>)

тогда как он будет работать с

typeof(GenericClass<SomeType>)

Следовательно, следующий код будет работать для любого производного GenericClass ‹>, если вы хотите протестировать на основе SomeType:

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

Единственный раз, когда я могу представить, что вы захотите протестировать GenericClass ‹>, - это сценарий фреймворка плагина.


Мысли об операторе "есть"

Во время разработки C # не допускает использование универсальных шаблонов без параметров, потому что они, по сути, не являются полным типом среды CLR на этом этапе. Следовательно, вы должны объявлять общие переменные с параметрами, и именно поэтому оператор «is» настолько эффективен для работы с объектами. Кстати, оператор is также не может оценивать универсальные типы без параметров.

Оператор «is» проверяет всю цепочку наследования, включая интерфейсы.

Итак, с учетом экземпляра любого объекта следующий метод поможет:

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

Это вроде как излишне, но я решил, что пойду вперед и визуализирую это для всех.

Данный

var t = new Test();

Следующие строки кода вернут истину:

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

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

bool test3 = IsTypeof<Test>(t);

С другой стороны, если вам нужно что-то конкретное для GenericClass, вы могли бы сделать его более конкретным, я полагаю, например:

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

Тогда вы бы протестировали так:

bool test1 = IsTypeofGenericClass<SomeType>(t);
person EnocNRoll - AnandaGopal Pardue    schedule 19.01.2009
comment
+1 за анализ и тестирование. Кроме того, ваш ответ был очень полезен в моем случае. - person Guillermo Gutiérrez; 07.09.2012
comment
Следует отметить, что компилятор полностью доволен .IsSubclassOf (typeof (GenericClass ‹›)), он просто не делает того, что вы могли бы пожелать. - person user2880616; 14.07.2017
comment
Но что, если SomeType неизвестен во время компиляции? - person Ryan The Leach; 24.10.2018
comment
Смысл всего обсуждения был о том, когда у вас есть только объект Type. - person Jonathan Wood; 28.07.2019
comment
@JonathanWood, поэтому мой ответ выше касается оператора typeof. Согласно документам: оператор typeof используется для получения объекта System.Type для типа. - person EnocNRoll - AnandaGopal Pardue; 29.04.2020

Я проработал некоторые из этих образцов и обнаружил, что в некоторых случаях они отсутствуют. Эта версия работает со всеми типами дженериков: типами, интерфейсами и их определениями.

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

Вот и модульные тесты:

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);
}
person fir3rpho3nixx    schedule 04.02.2011
comment
Это был ответ, который я искал! Работает для конкретных типов И для интерфейсов, спасибо :-) - person Menno Deij - van Rijswijk; 03.09.2012
comment
Я озадачен методом ResolveGenericTypeDefinition. Переменной shouldUseGenericType действительно присвоено значение: !parent.IsGenericType || parent.GetGenericTypeDefinition() == parent; Таким образом, вы заменяете эту переменную расширением оператора if: if (parent.IsGenericType && shouldUseGenericType), и вы получаете if (parent.IsGenericType && (!parent.IsGenericType || parent.GetGenericTypeDefinition() == parent)), которое затем сокращается до if (parent.IsGenericType && parent.GetGenericTypeDefinition() == parent)) parent = parent.GetGenericTypeDefinition(); - person Michael Blackburn; 05.12.2012
comment
Что, казалось бы, ничего не дает. Если бы это были типы значений, которые были бы сродни int j = 0; if (j is an int && j == 0) { j=0; } Могу ли я перепутать мои ссылочные равные и равные значения? Я думал, что в памяти есть только один экземпляр каждого типа, поэтому две переменные, указывающие на один и тот же тип, на самом деле указывают на одно и то же место в памяти. - person Michael Blackburn; 05.12.2012
comment
@MichaelBlackburn на месте :) Я изменил это так, чтобы он был просто: return parent.IsGenericType? parent.GetGenericTypeDefinition (): родительский; - person AaronHS; 19.02.2013
comment
если он уже совпадает с родительским, просто верните его! и он слишком много раз вызывает GetGenericTypeDef. Просто нужно позвонить один раз - person AaronHS; 19.02.2013
comment
Извините, я добавлю новый комментарий ниже. - person Menno Deij - van Rijswijk; 12.03.2013

Мне кажется, что эта реализация работает в большем количестве случаев (общий класс и интерфейс с инициированными параметрами или без них, независимо от количества дочерних элементов и параметров):

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

Вот мои тестовые примеры 70 76:

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

Классы и интерфейсы для тестирования:

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 { }
person Xav987    schedule 16.09.2013
comment
Это единственное решение, которое у меня сработало. Я не смог найти других решений, которые работали бы с классами, имеющими несколько параметров типа. Спасибо. - person Connor Clark; 03.01.2015
comment
Мне очень понравилось, что вы разместили все эти тестовые примеры. Я действительно думаю, что случаи 68 и 69 должны быть ложными, а не истинными, поскольку у вас есть ClassB, ClassA слева и ClassA, ClassB справа. - person Grax32; 08.01.2015
comment
Ты прав, @Grax. Сейчас у меня нет времени вносить исправления, но я обновлю свой пост, как только это будет сделано. Я думаю, что исправление должно быть в методе VerifyGenericArguments - person Xav987; 03.02.2015
comment
@Grax: Я нашел время, чтобы исправить это. Я добавил класс ClassB2, я изменил VerifyGenericArguments и добавил элемент управления при вызове VerifyGenericArguments. Я также изменил случаи 68 и 69 и добавил 68-2, 68-3, 68-4, 69-2, 69-3 и 69-4. - person Xav987; 16.03.2015
comment
Спасибо, сэр. +1 за рабочее решение И огромное количество тестовых примеров (по крайней мере, потрясающих для меня). - person Eldoïr; 24.04.2018
comment
Какой отличный ответ. Мне только жаль, что я не могу проголосовать дальше. - person Ian Kemp; 28.06.2019
comment
Просто добавляю свою благодарность за предоставление единственного решения, которое сработало для меня, и мою признательность за тестовые примеры. Отличная работа. - person JamesQMurphy; 25.02.2021

Код JaredPar работает, но только для одного уровня наследования. Для неограниченного уровня наследования используйте следующий код

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);
    }
}
person user53564    schedule 19.01.2009
comment
while в коде JaredPar охватывает неограниченное количество уровней. - person Jay; 19.03.2014
comment
@jay ... и избегает рекурсии. - person Marc L.; 24.01.2017
comment
@MarcL. При этом используется хвостовая рекурсия, поэтому компилятор должен легко оптимизировать рекурсию. - person AVH; 27.09.2017

Вот небольшой метод, который я создал для проверки того, что объект является производным от определенного типа. У меня отлично работает!

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);
}
person Community    schedule 20.03.2009

Это может быть излишним, но я использую такие методы расширения, как следующие. Они проверяют интерфейсы, а также подклассы. Он также может возвращать тип, имеющий указанное универсальное определение.

Например. для примера в вопросе он может тестировать как общий интерфейс, так и общий класс. Возвращенный тип может использоваться с GetGenericArguments, чтобы определить, что тип универсального аргумента - «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;
}
person codybartfast    schedule 22.05.2009
comment
Хороший пост! Приятно разделить проблемы на два метода. - person Wiebe Tijsma; 12.10.2011

Все это легко сделать с помощью linq. Это найдет любые типы, которые являются подклассом универсального базового класса GenericBaseType.

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

    IEnumerable<Type> mySubclasses = allTypes.Where(t => t.BaseType != null 
                                                            && t.BaseType.IsGenericType
                                                            && t.BaseType.GetGenericTypeDefinition() == typeof(GenericBaseType<,>));
person DVK    schedule 19.02.2015
comment
Это было единственное решение, которое сработало для меня. Просто и элегантно. Спасибо. - person silkfire; 20.04.2017
comment
Работает на. Небольшое примечание: == typeof (GenericBaseType ‹,›) для дженериков с двумя типами, == typeof (GenericBaseType ‹›) для дженериков с ограничением одного типа. - person dba; 19.07.2021

Добавлено в ответ @ jaredpar, вот что я использую для проверки интерфейсов:

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
person vexe    schedule 19.09.2014
comment
Хорошее дополнение. Проголосовал за вас и Джаредпара. Мой единственный комментарий: вы изменили положение типов (из ответа jaredpar) из-за метода расширения. Я удалил его как метод расширения, и это меня немного сбило с толку. Не твоя проблема, а моя. Просто хотел предупредить следующего человека. Спасибо еще раз. - person Tony; 18.06.2015
comment
@ Тони, спасибо за совет, но в следующий раз обновите ответ. - person johnny 5; 20.09.2017
comment
@vexe, важно тестирование, ваш исходный ответ не работает, он работает только потому, что вы тестировали его на интерфейсе. Во-вторых, вы тратите вычислительную мощность, выполняя эту функцию независимо от типа. - person johnny 5; 20.09.2017

Основываясь на отличном ответе, приведенном выше fir3rpho3nixx и Дэвидом Шмиттом, я изменил их код и добавил тест ShouldInheritOrImplementTypedGenericInterface (последний).

    /// <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>)));
    } 
person Menno Deij - van Rijswijk    schedule 12.03.2013

Простое решение: просто создайте и добавьте второй неуниверсальный интерфейс к универсальному классу:

public interface IGenericClass
{
}

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

Затем просто проверьте это любым удобным для вас способом, используя is, as, IsAssignableFrom и т. Д.

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

Очевидно, это возможно только в том случае, если у вас есть возможность редактировать общий класс (который, похоже, есть в OP), но он немного более элегантен и удобочитаем, чем использование загадочного метода расширения.

person kad81    schedule 05.12.2016
comment
однако простая проверка того, относится ли что-то к типу IGenericClass, не гарантирует, что GenericClass или GenericInterface действительно расширены или реализованы. Это означает, что ваш компилятор также не позволит вам получить доступ к каким-либо членам универсального класса. - person B12Toaster; 24.06.2018
comment
Я действительно удивлен, что это решение настолько низкое ... Я бы сделал небольшое изменение - я бы сделал интерфейс внутренним. Такой вид гарантирует, что это эквивалент. Без сумасшедшего проксирования никакая внешняя сторона не смогла бы реализовать / расширить этот интерфейс. И ремонтопригодность такого поиска на высоте - сколько решений перечислено выше? И у скольких из них нет комментария о том, что в таком случае не работает? ;) - person marchewek; 11.09.2020

ДжаредПар,

Это не сработало для меня, если я передал typeof (type ‹>) как toCheck. Вот что я изменил.

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;
}
person Cirem    schedule 21.01.2010
comment
Решение JaredPar действительно работает для typeof(type<>) как toCheck. Также вам действительно нужна нулевая проверка, как в решении JaredPar. Более того, я не знаю, чего еще вы достигли, заменив cur == generic в его решении на cur.IsGenericType && generic.GetGenericTypeDefinition() == cur.GetGenericTypeDefinition(), кроме ограничения работы вашего метода только для общих типов. Другими словами, ничего подобного не получится, за исключением: IsSubclassOfRawGeneric(typeof(MyClass), typeof(MyClass<>)) - person nawfal; 09.05.2013

@EnocNRoll - Ответ Ананды Гопал интересен, но в случае, если экземпляр не создан заранее или вы хотите проверить определение общего типа, я бы предложил этот метод:

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

и используйте его как:

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

Существует четыре условных случая, когда и t (для тестирования), и d являются универсальными типами, и два случая охватываются t==d, которые (1) ни t, ни d не являются универсальным определением или ( 2) оба они являются общими определениями. В остальных случаях одно из них является общим определением, только когда d уже является общим определением, у нас есть шанс сказать a t это d, но не наоборот.

Он должен работать с произвольными классами или интерфейсами, которые вы хотите протестировать, и возвращать то, что, как если бы вы тестировали экземпляр этого типа с помощью оператора is.

person Ken Kin    schedule 20.12.2017

Вы можете попробовать это расширение

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

поздно к игре на этом ... у меня тоже есть еще одна перестановка ответа JarodPar.

вот Type.IsSubClassOf (Type) любезно предоставлен рефлектором:

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

из этого мы видим, что он не делает ничего слишком плохого и похож на итеративный подход JaredPar. Все идет нормально. вот моя версия (отказ от ответственности: не тщательно протестирован, поэтому дайте мне знать, если вы обнаружите проблемы)

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

в основном это просто метод расширения для System.Type - я сделал это, чтобы намеренно ограничить тип thisType конкретными типами, так как я непосредственно использую для запроса LINQ предикаты «where» для объектов типа. я уверен, что все вы, умные люди, могли бы использовать эффективный универсальный статический метод, если вам нужно :) код делает несколько вещей, которые код ответа не делает

  1. откройте это до общего "расширения" - я рассматриваю наследование (думаю, классы), а также реализацию (интерфейсы); имена методов и параметров изменены, чтобы лучше отразить это
  2. ввод нуль-валидации (meah)
  3. ввод того же типа (класс не может расширяться)
  4. выполнение короткого замыкания, если рассматриваемый тип является интерфейсом; поскольку GetInterfaces () возвращает все реализованные интерфейсы (даже те, которые реализованы в суперклассах), вы можете просто пройти через эту коллекцию, не взбираясь по дереву наследования.

остальное в основном такое же, как код JaredPar

person isandburn    schedule 17.11.2016