Type Checking в C#: typeof, GetType() или is — что и когда использовать?
Основные различия между typeof, GetType() и is
Давайте разберем каждый подход по порядку, начиная с самого простого.
1. typeof — статическая проверка на этапе компиляции
typeof — это оператор, который возвращает объект Type для известного на этапе компиляции типа. Он не требует экземпляра объекта:
Type stringType = typeof(string); // Всегда System.String
Type intType = typeof(int); // Всегда System.Int32
Важно: typeof не учитывает наследование. typeof(BaseClass) == typeof(DerivedClass) всегда false, даже если есть цепочка наследования.
2. GetType() — динамическая проверка во время выполнения
Метод GetType() вызывается на экземпляре объекта и возвращает его фактический тип во время выполнения:
object obj = "Hello";
Type runtimeType = obj.GetType(); // System.String
// Классический пример с наследованием:
class Animal { }
class Dog : Animal { }
Animal pet = new Dog();
Console.WriteLine(pet.GetType() == typeof(Animal)); // False! Фактический тип - Dog
Console.WriteLine(pet.GetType() == typeof(Dog)); // True
Критическое замечание: GetType() никогда не возвращает nullable тип. Если переменная имеет тип int? и содержит значение, GetType() вернет typeof(int). Если значение null — будет NullReferenceException.
3. is — проверка совместимости типов
Оператор is проверяет, можно ли преобразовать объект к указанному типу (учитывая наследование и интерфейсы):
object obj = "Hello";
bool isString = obj is string; // true
bool isObject = obj is object; // true (все типы наследуются от object)
bool isIComparable = obj is IComparable; // true (string реализует IComparable)
// С наследованием:
Animal pet = new Dog();
bool isAnimal = pet is Animal; // true
bool isDog = pet is Dog; // true
is также корректно работает с nullable типами:
int? x = 5;
bool isInt = x is int; // true (распаковка nullable)
bool isNull = x is null; // false
int? y = null;
bool isInt2 = y is int; // false
bool isNull2 = y is null; // true
Когда что использовать?
- typeof — когда нужно получить
Typeдля известного типа (например, для передачи в рефлексию, создания экземпляров через Activator, сравнения с известным типом без наследования). - GetType() — когда нужно узнать точный тип объекта во время выполнения (например, для логирования, сериализации, проверки на конкретный класс без учета наследования).
- is — в 90% случаев. Когда нужно проверить, является ли объект экземпляром определенного типа или его наследника. Особенно в связке с pattern matching:
if (obj is string s) { Console.WriteLine(s.Length); }.
Производительность
is быстрее, чем GetType() == typeof(), потому что:
GetType()требует вызова виртуального метода и получения объектаType.is— это IL-инструкцияisinst, которая выполняется очень быстро.typeof— это константа времени компиляции, но сравнение типов через==все равно медленнее, чемis.
Итоговые рекомендации
- Используйте
isдля проверки совместимости типов (наследование, интерфейсы) — это самый читаемый и производительный вариант. - Используйте
typeofкогда тип известен на этапе компиляции и вам нужен объектType(рефлексия, атрибуты). - Используйте
GetType()когда нужно получить точный тип объекта (не учитывая наследование), например для сравнения с конкретным классом. - Избегайте
obj.GetType() == typeof(SomeType)если можно использоватьis— это и быстрее, и безопаснее с nullable.
Дополнение: Pattern Matching в современном C#
В C# 7.0+ появился мощный pattern matching, который делает is еще более удобным. Рекомендую использовать его вместо старых подходов:
// Старый способ
if (obj is string)
{
string s = (string)obj;
Console.WriteLine(s.Length);
}
// Новый способ (C# 7+)
if (obj is string s)
{
Console.WriteLine(s.Length); // s уже объявлена и приведена
}
// Проверка на null
if (obj is null) { /* obj == null */ }
// Проверка с дополнительным условием (C# 9+)
if (obj is string { Length: > 5 } longString)
{
Console.WriteLine($"Длинная строка: {longString}");
}
// Switch expression с типами
string result = obj switch
{
int i => $"Целое: {i}",
string str => $"Строка: {str}",
null => "null",
_ => "Другой тип"
};
Также стоит помнить, что typeof и GetType() могут давать разные результаты для generic-типов. Например, typeof(List<>) — это открытый generic, а new List<int>().GetType() — закрытый (List<int>). Для generic-типов лучше использовать is или проверку через GetGenericTypeDefinition().
И последнее: никогда не используйте GetType() на потенциально null-объектах без проверки — это гарантированный NullReferenceException.
Ответов пока нет.
Войдите чтобы оставить ответ