Как проверить, является ли тип подтипом ИЛИ типом объекта?

335

Чтобы проверить, является ли тип подклассом другого типа в C #, легко:

typeof (SubClass).IsSubclassOf(typeof (BaseClass)); // returns true

Однако это не удастся:

typeof (BaseClass).IsSubclassOf(typeof (BaseClass)); // returns false

Есть ли способ проверить, является ли тип подклассом ИЛИ самого базового класса, без использования ORоператора или метода расширения?

Даниэль Т.
источник

Ответы:

499

Видимо, нет.

Вот варианты:

Type.IsSubclassOf

Как вы уже узнали, это не будет работать, если два типа одинаковы, вот пример программы LINQPad, которая демонстрирует:

void Main()
{
    typeof(Derived).IsSubclassOf(typeof(Base)).Dump();
    typeof(Base).IsSubclassOf(typeof(Base)).Dump();
}

public class Base { }
public class Derived : Base { }

Вывод:

True
False

Что указывает на то, что Derivedэто подкласс Base, но это Base(очевидно) не подкласс сам по себе.

Type.IsAssignableFrom

Теперь это ответит на ваш конкретный вопрос, но также даст вам ложные срабатывания. Как отметил Эрик Липперт в комментариях, хотя метод действительно вернется Trueдля двух вышеупомянутых вопросов, он также вернется Trueдля них, которые вы, вероятно, не хотите:

void Main()
{
    typeof(Base).IsAssignableFrom(typeof(Derived)).Dump();
    typeof(Base).IsAssignableFrom(typeof(Base)).Dump();
    typeof(int[]).IsAssignableFrom(typeof(uint[])).Dump();
}

public class Base { }
public class Derived : Base { }

Здесь вы получите следующий вывод:

True
True
True

Последнее Trueуказывало бы, если бы метод отвечал только на заданный вопрос, что uint[]наследует int[]или что они одного типа, что явно не так.

Так что IsAssignableFromэто не совсем правильно.

is и as

«Проблема» isи asв контексте вашего вопроса заключается в том, что они потребуют от вас работать с объектами и писать один из типов непосредственно в коде, а не работать с Typeобъектами.

Другими словами, это не скомпилируется:

SubClass is BaseClass
^--+---^
   |
   +-- need object reference here

и не будет этого:

typeof(SubClass) is typeof(BaseClass)
                    ^-------+-------^
                            |
                            +-- need type name here, not Type object

и не будет этого:

typeof(SubClass) is BaseClass
^------+-------^
       |
       +-- this returns a Type object, And "System.Type" does not
           inherit from BaseClass

Вывод

Хотя приведенные выше методы могут соответствовать вашим потребностям, единственный правильный ответ на ваш вопрос (на мой взгляд) заключается в том, что вам потребуется дополнительная проверка:

typeof(Derived).IsSubclassOf(typeof(Base)) || typeof(Derived) == typeof(Base);

что, конечно, имеет больше смысла в методе:

public bool IsSameOrSubclass(Type potentialBase, Type potentialDescendant)
{
    return potentialDescendant.IsSubclassOf(potentialBase)
           || potentialDescendant == potentialBase;
}
Лассе В. Карлсен
источник
2
Спасибо! Я отмечу это как правильный ответ (нужно подождать еще 8 минут), так как вы упомянули, что проверка должна быть отменена, и предоставили ссылку на документацию MSDN.
Даниэль Т.
71
Обратите внимание, что это на самом деле не делает то, что задал вопрос; это не определяет, является ли один тип подклассом другого, а скорее, совместим ли один тип с другим. Массив uint не является подклассом массива int, но они совместимы по присваиванию. IEnumerable <Giraffe> не является подклассом IEnumerable <Animal>, но они совместимы по назначению в v4.
Эрик Липперт
Там нет никакого способа сделать это в имени метода, как Java? `` `пустота <? расширяет Base> saveObject (? objectToSave) `` `
Оливер Диксон
2
Как бы IsInstanceOfTypeвписаться в это?
Леннарт
Может быть заманчиво преобразовать IsSameOrSubclass в метод расширения, но я рекомендую против него, это немного неудобно для чтения и записи, и его легко испортить (изменение порядка потенциальныхBase и потенциальныхDescendant может быть смертельно опасным).
JRH
36
typeof(BaseClass).IsAssignableFrom(unknownType);
Марк Гравелл
источник
1

Если вы пытаетесь сделать это в проекте Xamarin Forms PCL, использование приведенных выше решений IsAssignableFromдает ошибку:

Ошибка: «Тип» не содержит определения для «IsAssignableFrom», и метод расширения «IsAssignableFrom», принимающий первый аргумент типа «Тип», не может быть найден (отсутствует директива using или ссылка на сборку?)

потому что IsAssignableFromпросит TypeInfoобъект. Вы можете использовать GetTypeInfo()метод из System.Reflection:

typeof(BaseClass).GetTypeInfo().IsAssignableFrom(typeof(unknownType).GetTypeInfo())

Бруно Серрано
источник
0

Я публикую этот ответ с надеждой, что кто-то поделится со мной, если и почему это будет плохой идеей. В моем приложении у меня есть свойство Type, которое я хочу проверить, чтобы убедиться, что это typeof (A) или typeof (B), где B - это любой класс, производный от A. Итак, мой код:

public class A
{
}

public class B : A
{
}

public class MyClass
{
    private Type _helperType;
    public Type HelperType
    {
        get { return _helperType; }
        set 
        {
            var testInstance = (A)Activator.CreateInstance(value);
            if (testInstance==null)
                throw new InvalidCastException("HelperType must be derived from A");
            _helperType = value;
        }
    }
}

Я чувствую, что могу быть немного наивным, поэтому любые отзывы приветствуются.

baskren
источник
2
Есть несколько проблем с этой идеей: 1) типу нужен конструктор без параметров, иначе CreateInstance завершится с ошибкой; 2) приведение к (A) не возвращает ноль, если приведение не может быть выполнено, оно бросает; 3) новый экземпляр вам на самом деле не нужен, поэтому у вас бесполезное распределение. Принятый ответ лучше (хотя и не идеален).
Марсель Попеску
Спасибо за ответ. Очень полезно.
Baskren
@baskren, возможно, тогда оставь свой полезный комментарий. Я был upvote # 1.
Developer63