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 ojest Dog, nie Animal. Możesz jednak sprawić, aby to zadziałało, jeśli użyjesz IsAssignableFrommetody Typeklasy.
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 issłowa kluczowego. Dlatego jeśli takie zachowanie jest pożądane, należy użyć issł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 issł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ć isoperatora, a jeśli otak Animal, to sprawdzimy ponownie, aby zweryfikować rzut.
Zamiast tego bardziej efektywnie jest to zrobić:
Animal a = o as Animal;
if(a != null)
a.Speak();
asOperator 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 obę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 InvalidCastExceptionzamiast 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 osię za Animalkażdym razem, ale nie jest to oczywiste w konstruktorze, w którym asużywana jest obsada. Nie jest to oczywiste, dopóki nie przejdziesz do Interactmetody, w której animalpole 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 asi sprawdź null.
Jeśli chcesz traktować obiekt jako instancję określonego typu, a obiekt ma być tego typu, użyj zwykłego rzutowania.
as!