Type t = typeof(obj1);
if (t == typeof(int))
// Some code here
Это ошибка. Оператор typeof в C # может принимать только имена типов, но не объекты.
if (obj1.GetType() == typeof(int))
// Some code here
Это будет работать, но, возможно, не так, как вы ожидаете. Для типов значений, как вы показали здесь, это приемлемо, но для ссылочных типов он будет возвращать true, только если тип был точно такого же типа , а не что-то еще в иерархии наследования. Например:
class Animal{}
class Dog : Animal{}
static void Foo(){
object o = new Dog();
if(o.GetType() == typeof(Animal))
Console.WriteLine("o is an animal");
Console.WriteLine("o is something else");
}
Это вывело бы "o is something else"
, потому что тип o
равен Dog
, а не Animal
. Однако вы можете выполнить эту работу, если используете метод IsAssignableFrom
класса Type
.
if(typeof(Animal).IsAssignableFrom(o.GetType())) // note use of tested type
Console.WriteLine("o is an animal");
Однако эта техника все еще оставляет большую проблему. Если ваша переменная равна нулю, вызов GetType()
вызовет исключение NullReferenceException. Чтобы заставить его работать правильно, вы должны сделать:
if(o != null && typeof(Animal).IsAssignableFrom(o.GetType()))
Console.WriteLine("o is an animal");
При этом вы получаете эквивалентное поведение ключевого слова is
. Следовательно, если вы хотите именно такое поведение, вам следует использовать ключевое слово is
, которое более читабельно и более эффективно.
if(o is Animal)
Console.WriteLine("o is an animal");
Однако в большинстве случаев ключевое слово is
не совсем то, что вам действительно нужно, потому что обычно недостаточно просто знать, что объект имеет определенный тип. Обычно вы хотите использовать этот объект как экземпляр этого типа, что также требует его приведения. И поэтому вы можете написать себе такой код:
if(o is Animal)
((Animal)o).Speak();
Но это заставляет CLR проверять тип объекта до двух раз. Он проверит его один раз, чтобы удовлетворить оператор is
, и если o
действительно является Animal
, мы сделаем это снова, чтобы проверить приведение.
Вместо этого эффективнее:
Animal a = o as Animal;
if(a != null)
a.Speak();
Оператор as
- это приведение, которое не сгенерирует исключение в случае сбоя, вместо этого возвращает null
. Таким образом, CLR проверяет тип объекта только один раз, и после этого нам просто нужно выполнить нулевую проверку, что более эффективно.
Но будьте осторожны: многие люди попадают в ловушку с as
. Поскольку он не генерирует исключения, некоторые люди думают о нем как о «безопасном» касте, и они используют его исключительно, избегая регулярных кастований. Это приводит к таким ошибкам:
(o as Animal).Speak();
В этом случае разработчик явно предполагает, что o
будет всегда быть Animal
, и, пока их допущения верны, все работает нормально. Но если они ошибаются, то в итоге они получат NullReferenceException
. При обычном приведении вместо этого они получили бы InvalidCastException
, что более правильно определило бы проблему.
Иногда эту ошибку бывает трудно найти:
class Foo{
readonly Animal animal;
public Foo(object o){
animal = o as Animal;
}
public void Interact(){
animal.Speak();
}
}
Это еще один случай, когда разработчик явно ожидает, что o
будет Animal
каждый раз, но это не очевидно в конструкторе, где используется приведение as
. Это не очевидно, пока вы не доберетесь до метода Interact
, где ожидается, что поле animal
будет назначено положительно. В этом случае вы не только получите ложное исключение, но оно не будет выдано до тех пор, пока потенциально не произойдет намного позже, чем произошла фактическая ошибка.
В итоге:
Если вам нужно только узнать, относится ли объект к какому-либо типу, используйте is
.
Если вам нужно обработать объект как экземпляр определенного типа, но вы точно не знаете, что объект будет этого типа, используйте as
и проверьте null
.
Если вам нужно обработать объект как экземпляр определенного типа, и объект должен быть этого типа, используйте обычное приведение.