Type Checking в C#: typeof, GetType или is? Как не запутаться в трёх соснах
Каждый C#-разработчик, рано или поздно, сталкивается с необходимостью проверить тип объекта. Казалось бы, что может быть проще? Но когда перед тобой три варианта — typeof, GetType() и is — глаза разбегаются, а в голове возникает закономерный вопрос: "А какой из них выбрать?".
Эта статья — не просто сухая выдержка из документации. Мы разберём каждый подход на реальных примерах, выявим подводные камни и дадим чёткие рекомендации. После прочтения вы перестанете гадать и будете точно знать, какой инструмент использовать в конкретной ситуации. Поехали!
Зачем вообще проверять типы в C#?
Прежде чем нырять в дебри, давайте вспомним, зачем нам это нужно. C# — строго типизированный язык, но это не отменяет ситуаций, когда тип объекта известен только во время выполнения. Вот самые частые сценарии:
- Работа с интерфейсами и базовыми классами: Вы получаете объект как
objectили базовый тип, а вам нужно узнать его реальный "характер". - Сериализация и десериализация: JSON или XML приходят в виде строки, и нужно восстановить конкретный тип.
- Рефлексия (Reflection): Когда вы динамически создаёте объекты или вызываете методы, зная только имя типа.
- Логирование и отладка: Иногда нужно просто вывести в лог, с каким типом вы сейчас работаете.
И вот тут на сцену выходят наши три героя. Давайте познакомимся с каждым поближе.
typeof — статический взгляд в будущее
typeof — это оператор, который возвращает объект System.Type для известного на этапе компиляции типа. Он работает как справочная служба: "Скажи мне имя класса, и я дам тебе всю информацию о нём".
Как это работает:
Type stringType = typeof(string);
Console.WriteLine(stringType.FullName); // Вывод: System.String
Type intType = typeof(int);
Console.WriteLine(intType.FullName); // Вывод: System.Int32
Ключевая особенность: typeof не требует экземпляра объекта. Это статическая операция. Вы говорите компилятору: "У меня есть тип MyClass, дай мне его метаданные".
Когда использовать:
- Когда вам нужен тип по имени класса, а не по объекту.
- В атрибутах (например,
[AttributeUsage(typeof(MyAttribute))]). - В дженериках:
typeof(List<>).
Подводный камень: typeof всегда возвращает точный тип, который вы указали в коде. Он не знает о наследовании. Если у вас есть class Dog : Animal { }, то typeof(Animal) никогда не станет typeof(Dog).
GetType() — динамическое расследование
В отличие от typeof, метод GetType() вызывается на экземпляре объекта. Он берёт реальный объект, лежащий в памяти, и говорит: "Ага, вот ты кто на самом деле!".
Пример:
object obj = "Привет, мир!";
Type typeOfObj = obj.GetType();
Console.WriteLine(typeOfObj.FullName); // Вывод: System.String
// А теперь хитрость:
object obj2 = 42;
Type typeOfObj2 = obj2.GetType();
Console.WriteLine(typeOfObj2.FullName); // Вывод: System.Int32
Обратите внимание: переменная obj объявлена как object, но GetType() "видит" сквозь эту обёртку и возвращает System.String. Это и есть динамическое определение типа.
Когда использовать:
- Когда вам нужно узнать точный тип объекта во время выполнения.
- Для сравнения типов:
obj.GetType() == typeof(MyClass). - В методах, которые принимают
objectи должны вести себя по-разному для разных типов.
Важное предостережение: GetType() никогда не врёт. Он всегда возвращает самый конкретный тип в цепочке наследования. Если у вас Dog : Animal, то dog.GetType() вернёт Dog, а не Animal. Исключение — если сам объект null. Вызов GetType() на null вызовет NullReferenceException.
is — умный и безопасный
Оператор is — это, пожалуй, самый элегантный и безопасный способ проверки типа в современном C#. Он не просто проверяет, является ли объект экземпляром указанного типа, но и может сразу привести его к этому типу.
Классическое использование:
object obj = "Текст";
if (obj is string)
{
Console.WriteLine("Это строка!");
}
Современный C# (7.0+): pattern matching с is
object obj = "Привет, C#!";
if (obj is string text)
{
// Переменная 'text' уже доступна и имеет тип string!
Console.WriteLine($"Длина строки: {text.Length}");
}
// Ещё круче — switch с pattern matching:
object value = 123;
switch (value)
{
case int i:
Console.WriteLine($"Целое число: {i}");
break;
case string s:
Console.WriteLine($"Строка: {s}");
break;
case null:
Console.WriteLine("Это null");
break;
}
Когда использовать:
- Когда вам нужно не только проверить тип, но и сразу работать с объектом как с этим типом.
- Для проверки на совместимость с интерфейсом:
if (obj is IDisposable). - В современных приложениях — это стандарт де-факто. Забудьте про
if (obj is string) { var s = (string)obj; }.
Подводный камень: is проверяет не только точное совпадение, но и совместимость по наследованию. Если Dog : Animal, то dog is Animal вернёт true. Это часто то, что нужно, но иногда может сбить с толку, если вам нужно строгое соответствие.
Сравнительная таблица: typeof vs GetType vs is
Чтобы было наглядно, вот сводка:
| Критерий | typeof | GetType() | is |
|---|---|---|---|
Требует экзем
БлогиРекомендаций по книгамЧитайте также |