Type t = typeof(obj1);
if (t == typeof(int))
// Some code here
To jest błąd. Operator typeof w języku C # może przyjmować tylko nazwy typów, a nie obiekty.
if (obj1.GetType() == typeof(int))
// Some code here
To zadziała, ale może nie tak, jak można się spodziewać. W przypadku typów wartości, jak pokazano tutaj, jest to akceptowalne, ale dla typów referencyjnych zwróciłoby wartość true tylko wtedy, gdy typ był dokładnie tego samego typu, a nie coś innego w hierarchii dziedziczenia. Na przykład:
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");
}
Byłoby to wydrukować "o is something else"
, ponieważ typ o
jest Dog
, nie Animal
. Możesz jednak sprawić, aby to zadziałało, jeśli użyjesz IsAssignableFrom
metody Type
klasy.
if(typeof(Animal).IsAssignableFrom(o.GetType())) // note use of tested type
Console.WriteLine("o is an animal");
Ta technika wciąż jednak stanowi poważny problem. Jeśli twoja zmienna ma wartość NULL, wywołanie GetType()
to wyrzuci NullReferenceException. Aby więc działało poprawnie, wykonaj następujące czynności:
if(o != null && typeof(Animal).IsAssignableFrom(o.GetType()))
Console.WriteLine("o is an animal");
Dzięki temu masz równoważne zachowanie is
słowa kluczowego. Dlatego jeśli takie zachowanie jest pożądane, należy użyć is
słowa kluczowego, które jest bardziej czytelne i wydajniejsze.
if(o is Animal)
Console.WriteLine("o is an animal");
Jednak w większości przypadków is
słowo kluczowe nadal nie jest tym, czego naprawdę chcesz, ponieważ zwykle nie wystarczy po prostu wiedzieć, że obiekt jest określonego typu. Zwykle chcesz faktycznie użyć tego obiektu jako instancji tego typu, co również wymaga jego rzutowania. I może się okazać, że piszesz taki kod:
if(o is Animal)
((Animal)o).Speak();
Ale to powoduje, że CLR sprawdza typ obiektu maksymalnie dwa razy. Sprawdzi to raz, aby zadowolić is
operatora, a jeśli o
tak Animal
, to sprawdzimy ponownie, aby zweryfikować rzut.
Zamiast tego bardziej efektywnie jest to zrobić:
Animal a = o as Animal;
if(a != null)
a.Speak();
as
Operator jest obsada, że nie rzuci wyjątek, jeśli to się nie powiedzie, zamiast wracać null
. W ten sposób CLR sprawdza typ obiektu tylko raz, a następnie musimy po prostu sprawdzić zero, co jest bardziej wydajne.
Ale uwaga: wiele osób wpada w pułapkę as
. Ponieważ nie rzuca wyjątków, niektórzy uważają go za „bezpieczną” obsadę i używają jej wyłącznie, unikając regularnych rzutów. Prowadzi to do takich błędów:
(o as Animal).Speak();
W tym przypadku, deweloper jest wyraźnie zakładając, że o
będzie zawsze być Animal
, i tak długo, jak ich założenie jest słuszne, wszystko działa bez zarzutu. Ale jeśli się mylą, to kończą się tutaj NullReferenceException
. W przypadku zwykłej obsady otrzymaliby InvalidCastException
zamiast tego, co bardziej poprawnie zidentyfikowałoby problem.
Czasami ten błąd może być trudny do znalezienia:
class Foo{
readonly Animal animal;
public Foo(object o){
animal = o as Animal;
}
public void Interact(){
animal.Speak();
}
}
Jest to kolejny przypadek, w którym deweloper wyraźnie spodziewa o
się za Animal
każdym razem, ale nie jest to oczywiste w konstruktorze, w którym as
używana jest obsada. Nie jest to oczywiste, dopóki nie przejdziesz do Interact
metody, w której animal
pole ma zostać pozytywnie przypisane. W tym przypadku nie tylko trafiasz na wprowadzający w błąd wyjątek, ale jest on zgłaszany dopiero potencjalnie znacznie później niż wtedy, gdy wystąpił rzeczywisty błąd.
W podsumowaniu:
Jeśli potrzebujesz tylko wiedzieć, czy obiekt jest jakiegoś typu, użyj is
.
Jeśli musisz traktować obiekt jako instancję określonego typu, ale nie wiesz na pewno, że obiekt będzie tego typu, użyj as
i sprawdź null
.
Jeśli chcesz traktować obiekt jako instancję określonego typu, a obiekt ma być tego typu, użyj zwykłego rzutowania.
as
!