Type t = typeof(obj1);
if (t == typeof(int))
// Some code here
Це помилка. Оператор typeof в C # може приймати лише імена типів, а не об'єкти.
if (obj1.GetType() == typeof(int))
// Some code here
Це спрацює, але, можливо, не так, як ви очікували. Для типів значень, як ви показали тут, це прийнятно, але для посилальних типів воно поверне справжнє лише, якщо тип був би точно таким же типом, а не чимось іншим в ієрархії спадкування. Наприклад:
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
.
Якщо вам потрібно розглянути об'єкт як екземпляр певного типу, а об'єкт повинен бути такого типу, використовуйте звичайний склад.
as
!