Type t = typeof(obj1);
if (t == typeof(int))
// Some code here
Isto é um erro. O operador typeof em C # pode usar apenas nomes de tipos, não objetos.
if (obj1.GetType() == typeof(int))
// Some code here
Isso funcionará, mas talvez não como você esperaria. Para os tipos de valor, como você mostrou aqui, é aceitável, mas para os tipos de referência, ele só retornará true se o tipo for exatamente o mesmo tipo, e não outra coisa na hierarquia de herança. Por exemplo:
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");
}
Isso imprimiria "o is something else"
, porque o tipo de o
é Dog
, não Animal
. Você pode fazer isso funcionar, no entanto, se você usar o IsAssignableFrom
método da Type
classe.
if(typeof(Animal).IsAssignableFrom(o.GetType())) // note use of tested type
Console.WriteLine("o is an animal");
Essa técnica ainda deixa um grande problema, no entanto. Se sua variável for nula, a chamada para GetType()
lançará uma NullReferenceException. Então, para fazê-lo funcionar corretamente, você faria:
if(o != null && typeof(Animal).IsAssignableFrom(o.GetType()))
Console.WriteLine("o is an animal");
Com isso, você tem um comportamento equivalente à is
palavra - chave. Portanto, se esse é o comportamento que você deseja, use a is
palavra - chave, que é mais legível e mais eficiente.
if(o is Animal)
Console.WriteLine("o is an animal");
Na maioria dos casos, porém, a is
palavra - chave ainda não é o que você realmente deseja, porque geralmente não é suficiente apenas saber que um objeto é de um determinado tipo. Geralmente, você deseja realmente usar esse objeto como uma instância desse tipo, o que exige a conversão também. E assim, você pode escrever código como este:
if(o is Animal)
((Animal)o).Speak();
Mas isso faz com que o CLR verifique o tipo do objeto até duas vezes. Ele irá checá-lo uma vez para satisfazer o is
operador e, se o
for de fato um Animal
, fazemos novamente o checagem para validar o elenco.
É mais eficiente fazer isso:
Animal a = o as Animal;
if(a != null)
a.Speak();
O as
operador é um elenco que não emitirá uma exceção se falhar, retornando null
. Dessa forma, o CLR verifica o tipo de objeto apenas uma vez e, depois disso, precisamos fazer uma verificação nula, que é mais eficiente.
Mas cuidado: muitas pessoas caem em uma armadilha as
. Como não gera exceções, algumas pessoas pensam nele como um elenco "seguro" e o usam exclusivamente, evitando os lançamentos regulares. Isso leva a erros como este:
(o as Animal).Speak();
Neste caso, o desenvolvedor está assumindo claramente que o
irá sempre ser uma Animal
, e desde que a sua hipótese é correta, tudo funciona bem. Mas se eles estão errados, então o que eles acabam com aqui é um NullReferenceException
. Com um elenco regular, eles teriam conseguido um InvalidCastException
lugar, o que teria identificado mais corretamente o problema.
Às vezes, esse bug pode ser difícil de encontrar:
class Foo{
readonly Animal animal;
public Foo(object o){
animal = o as Animal;
}
public void Interact(){
animal.Speak();
}
}
Este é outro caso em que o desenvolvedor espera claramente o
estar Animal
sempre, mas isso não é óbvio no construtor, onde o as
elenco é usado. Não é óbvio até que você chegue ao Interact
método, onde animal
se espera que o campo seja atribuído positivamente. Nesse caso, você não apenas acaba com uma exceção enganosa, mas é lançada até potencialmente muito mais tarde do que quando ocorreu o erro real.
Em suma:
Se você só precisa saber se um objeto é ou não de algum tipo, use is
.
Se você precisar tratar um objeto como uma instância de um determinado tipo, mas não tiver certeza de que o objeto será desse tipo, use as
e verifique null
.
Se você precisar tratar um objeto como uma instância de um determinado tipo, e o objeto for desse tipo, use uma conversão regular.
as
!