Type t = typeof(obj1);
if (t == typeof(int))
// Some code here
Esto es un error El operador typeof en C # solo puede tomar nombres de tipos, no objetos.
if (obj1.GetType() == typeof(int))
// Some code here
Esto funcionará, pero quizás no como era de esperar. Para los tipos de valor, como ha mostrado aquí, es aceptable, pero para los tipos de referencia, solo devolvería verdadero si el tipo era exactamente el mismo tipo, no otra cosa en la jerarquía de herencia. Por ejemplo:
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");
}
Esto se imprimiría "o is something else"
, porque el tipo de o
es Dog
, no Animal
. Sin embargo, puede hacer que esto funcione si usa el IsAssignableFrom
método de la Type
clase.
if(typeof(Animal).IsAssignableFrom(o.GetType())) // note use of tested type
Console.WriteLine("o is an animal");
Sin embargo, esta técnica aún deja un gran problema. Si su variable es nula, la llamada a GetType()
arrojará una NullReferenceException. Entonces, para que funcione correctamente, harías:
if(o != null && typeof(Animal).IsAssignableFrom(o.GetType()))
Console.WriteLine("o is an animal");
Con esto, tiene un comportamiento equivalente de la is
palabra clave. Por lo tanto, si este es el comportamiento que desea, debe usar la is
palabra clave, que es más legible y más eficiente.
if(o is Animal)
Console.WriteLine("o is an animal");
Sin embargo, en la mayoría de los casos, la is
palabra clave todavía no es lo que realmente desea, porque generalmente no es suficiente saber que un objeto es de cierto tipo. Por lo general, desea utilizar ese objeto como una instancia de ese tipo, que también requiere lanzarlo. Y entonces puede que te encuentres escribiendo código como este:
if(o is Animal)
((Animal)o).Speak();
Pero eso hace que el CLR verifique el tipo de objeto hasta dos veces. Lo verificará una vez para satisfacer al is
operador, y si de o
hecho es un Animal
, lo haremos verificar nuevamente para validar el lanzamiento.
Es más eficiente hacer esto en su lugar:
Animal a = o as Animal;
if(a != null)
a.Speak();
El as
operador es un elenco que no lanzará una excepción si falla, sino que regresará null
. De esta manera, el CLR verifica el tipo de objeto solo una vez, y después de eso, solo necesitamos hacer una verificación nula, que es más eficiente.
Pero cuidado: muchas personas caen en una trampa con as
. Debido a que no arroja excepciones, algunas personas lo consideran un yeso "seguro", y lo usan exclusivamente, evitando los lanzamientos regulares. Esto lleva a errores como este:
(o as Animal).Speak();
En este caso, el desarrollador está asumiendo claramente que o
habrá siempre ser una Animal
, y siempre que su suposición es correcta, todo funciona bien. Pero si están equivocados, entonces lo que terminan aquí es a NullReferenceException
. Con un reparto regular, habrían obtenido un InvalidCastException
en su lugar, que habría identificado más correctamente el problema.
A veces, este error puede ser difícil de encontrar:
class Foo{
readonly Animal animal;
public Foo(object o){
animal = o as Animal;
}
public void Interact(){
animal.Speak();
}
}
Este es otro caso en el que el desarrollador claramente espera o
serlo Animal
cada vez, pero esto no es obvio en el constructor, donde as
se usa el elenco. No es obvio hasta que llegue al Interact
método, donde animal
se espera que el campo se asigne positivamente. En este caso, no solo termina con una excepción engañosa, sino que no se produce hasta potencialmente mucho más tarde que cuando ocurrió el error real.
En resumen:
Si solo necesita saber si un objeto es o no de algún tipo, úselo is
.
Si necesita tratar un objeto como una instancia de cierto tipo, pero no está seguro de que el objeto sea de ese tipo, utilícelo as
y verifique null
.
Si necesita tratar un objeto como una instancia de cierto tipo, y se supone que el objeto es de ese tipo, use un molde regular.
as
!