Як перевірити, чи є тип підтипу АБО тип об’єкта?


335

Щоб перевірити, чи є тип підкласу іншого типу в C #, легко:

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

Однак це не вдасться:

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

Чи є спосіб перевірити, чи тип є або підкласом АБО базового класу, не використовуючи ORоператора чи використовуючи метод розширення?

Відповіді:


499

Мабуть, ні.

Ось варіанти:

Тип.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 в метод розширення може бути заманливо, але я рекомендую проти цього, читати і писати трохи незручно і легко зіпсувати (змінивши порядок potencijalBase і potencijalDescendant може бути смертельним).
jrh



1

Якщо ви намагаєтеся зробити це в PCL-проекті Xamarin Forms, наведені вище рішення використовують IsAssignableFromпомилку:

Помилка: 'Тип' не містить визначення для 'IsAssignableFrom', і жодного методу розширення 'IsAssignableFrom', який приймає перший аргумент типу 'Тип', не вдалося знайти (ви не маєте використання директиви чи посилання на збірку?)

тому що 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;
        }
    }
}

Я відчуваю, що я тут трохи наївний, тому будь-які відгуки будуть вітатися.


2
З цією ідеєю є декілька питань: 1) типу потрібен конструктор без параметрів, або CreateInstance вийде з ладу; 2) кастинг на (A) не повертається до нуля, якщо амплуа неможливо зробити, він кидає; 3) вам фактично не потрібен новий екземпляр, тому у вас є марне виділення. Прийнята відповідь краще (хоча і не досконала).
Марсель Попеску

Дякуємо за відгук. Дуже корисний.
баскрен

@baskren, можливо, тоді підтримає його корисний коментар. Я був висунутий №1.
Developer63
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.