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


309

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

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

public class Test : GenericClass<SomeType>
{
}

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

t.IsSubclassOf(typeof(GenericClass<>))

не работает.

Ответы:


430

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

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
Это приятный кусок кода, я должен сказать. Реализация цикла while позволяет избежать ненужного снижения производительности рекурсии. Это элегантное и красивое решение мета-общего вопроса.
EnocNRoll - AnandaGopal Pardue

2
Я добавил этот метод в мой статический класс ReflectionUtils в моей среде и также адаптировал его как метод расширения для объекта, определив в методе toCheck как Type toCheck = obj.GetType (); данный «объект» является первым параметром.
EnocNRoll - AnandaGopal Pardue

11
Цикл while не прервется, если тип toCheck не является классом (т. Е. Интерфейсом). Это вызовет исключение NullReferenceException.
JD Courtoy

2
Это также вернет true, если toCheck является универсальным типом, который вы ищете.
Oillio

14
Это работает только для конкретного типа наследования ... Тестовый пример: bool Ожидается = истина; bool actual = Program.IsSubclassOfRawGeneric (typeof (IEnumerable <>), typeof (List <string>)); Assert.AreEqual (ожидаемый, фактический); // не удается
Бобби

90

(Перемещено из-за массивного переписывания)

Кодовый ответ 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 проверяет всю цепочку наследования, включая интерфейсы.

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

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

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

Дано

var t = new Test();

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

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

2
+1 за анализ и тестирование. Кроме того, ваш ответ был очень полезен в моем случае.
Гильермо Гутьеррес

3
Следует отметить, что компилятор полностью удовлетворен .IsSubclassOf (typeof (GenericClass <>)), он просто не делает то, что вы могли бы пожелать.
user2880616

2
Но что, если SomeType не известен во время компиляции?
Райан Лич

Смысл всей дискуссии был о том, когда у вас есть только Typeобъект.
Джонатан Вуд

@JonathanWood, поэтому мой ответ выше касается typeofоператора. Согласно документации: «Оператор typeof используется для получения объекта System.Type для типа».
EnocNRoll - AnandaGopal Pardue

33

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

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

2
Я озадачен по поводу метода 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();
Майкл Блэкберн

2
Который, казалось бы, ничего не делает. Если бы это были типы значений, которые были бы похожи на int j = 0; if (j is an int && j == 0) { j=0; } то, перепутал ли я мои эталонные значения и значения? Я думал, что в памяти был только один экземпляр каждого типа, поэтому две переменные, которые указывают на один и тот же тип, фактически указывают на одно и то же место в памяти.
Майкл Блэкберн

1
@MichaelBlackburn пятно на :) я рефакторинг это просто: вернуть родителя. IsGenericType? parent.GetGenericTypeDefinition (): parent;
AaronHS

3
если он уже такой же, как родитель, просто верните его! и он слишком часто вызывает GetGenericTypeDef. Это просто нужно вызвать один раз
AaronHS

1
Извините, я добавлю новый комментарий ниже.
Менно Дей - ван Рейсвейк,

26

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

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

4
Это единственное решение, которое сработало для меня. Я не мог найти никаких других решений, которые работали бы с классами, которые имеют несколько параметров типа. Спасибо.
Коннор Кларк

1
Я очень признателен за публикацию всех этих тестов. Я действительно считаю, что случаи 68 и 69 должны быть ложными, а не истинными, поскольку у вас есть ClassB, ClassA слева и ClassA, ClassB справа.
Grax32

Вы правы, @Grax. У меня нет времени, чтобы сделать исправление сейчас, но я обновлю свой пост, как только это будет сделано. Я думаю, что исправление должно быть в методе "VerifyGenericArguments"
Xav987

1
@Grax: я нашел время, чтобы сделать исправление. Я добавил класс ClassB2, изменил VerifyGenericArguments и добавил элемент управления при вызове VerifyGenericArguments. Я также изменил случаи 68 и 69 и добавил 68-2, 68-3, 68-4, 69-2, 69-3 и 69-4.
Xav987

1
Спасибо, сэр. +1 за рабочий раствор И огромное количество контрольных примеров (по крайней мере, для меня).
Элдор

10

Код 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);
    }
}

4
Код whileв JaredPar охватывает неограниченные уровни.
Jay

@jay ... и избегает рекурсии.
Марк Л.

1
@MarcL. При этом используется хвостовая рекурсия, поэтому для компилятора должно быть тривиально оптимизировать рекурсию.
Дархуук

10

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

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

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

Например, для примера в вопросе он может проверять как универсальный интерфейс, так и универсальный класс. Возвращаемый тип может использоваться с, 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;
}

Хороший пост! Приятно разделить заботы двумя способами.
Wiebe Tijsma

4

Основываясь на превосходном ответе 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>)));
    } 

4

Все это можно легко сделать с помощью 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<,>));

Это было единственное решение, которое сработало для меня. Просто и элегантно. Спасибо.
шелковый огонь

4

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

public interface IGenericClass
{
}

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

Тогда просто проверить , что в любом случае вы хотите , используя is, as, IsAssignableFromи т.д.

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

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


1
Однако, просто проверить , является ли нечто типа IGenericClassне гарантирует вам , что GenericClassи GenericInterfaceна самом деле расширены или реализованы. Это означает, что ваш компилятор также не позволит вам получить доступ к членам универсального класса.
B12Toaster

4

Добавлено в ответ @ 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);
}

Пример:

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

Хорошее дополнение. Дали и вам, и Джаредпару повышенный голос. Мой единственный комментарий - вы изменили положение типов (из ответа jaredpar) из-за метода расширения. Я удалил его как метод расширения, и он на некоторое время отбросил меня. Не твоя проблема, а моя. Просто хотел дать следующему человеку голову. Еще раз спасибо.
Тони

@ Тони, спасибо за совет, но в следующий раз Обновите ответ.
Джонни 5

@vexe, тестирование важно, ваш оригинальный ответ не работает, он работает только потому, что вы тестировали его на интерфейсе. Во-вторых, вы теряете вычислительную мощность, выполняя эту функцию независимо от типа.
Джонни 5

2

JaredPar,

Это не сработало для меня, если я передал 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;
}

Решение JaredPar действительно работает typeof(type<>)как toCheck. Также вам действительно нужна нулевая проверка, как в решении JaredPar. Кроме того, я не знаю, чего еще вы достигаете, заменив cur == genericв своем решении cur.IsGenericType && generic.GetGenericTypeDefinition() == cur.GetGenericTypeDefinition()другим, кроме ограничения своего метода, работающего только для универсальных типов. Другими словами, ничего подобного не получается за исключением:IsSubclassOfRawGeneric(typeof(MyClass), typeof(MyClass<>))
nawfal

2

@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уже есть общее определение, у нас есть шанс сказать, что tесть,d но не наоборот.

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


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

0

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

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

0

опоздал к игре об этом ... у меня тоже есть еще одна перестановка ответа 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" для объектов Type. я уверен, что все вы, умные люди, могли бы столкнуть его с эффективным, универсальным статическим методом, если вам нужно :) код делает несколько вещей, которых код ответа не делает

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.