Type t = typeof(obj1);
if (t == typeof(int))
// Some code here
This is an error. The typeof operator in C# can only take type names, not objects.
if (obj1.GetType() == typeof(int))
// Some code here
This will work, but maybe not as you would expect. For value types, as you've shown here, it's acceptable, but for reference types, it would only return true if the type was the exact same type, not something else in the inheritance hierarchy. For instance:
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");
}
This would print "o is something else"
, because the type of o
is Dog
, not Animal
. You can make this work, however, if you use the IsAssignableFrom
method of the Type
class.
if(typeof(Animal).IsAssignableFrom(o.GetType())) // note use of tested type
Console.WriteLine("o is an animal");
This technique still leaves a major problem, though. If your variable is null, the call to GetType()
will throw a NullReferenceException. So to make it work correctly, you'd do:
if(o != null && typeof(Animal).IsAssignableFrom(o.GetType()))
Console.WriteLine("o is an animal");
With this, you have equivalent behavior of the is
keyword. Hence, if this is the behavior you want, you should use the is
keyword, which is more readable and more efficient.
if(o is Animal)
Console.WriteLine("o is an animal");
In most cases, though, the is
keyword still isn't what you really want, because it's usually not enough just to know that an object is of a certain type. Usually, you want to actually use that object as an instance of that type, which requires casting it too. And so you may find yourself writing code like this:
if(o is Animal)
((Animal)o).Speak();
But that makes the CLR check the object's type up to two times. It will check it once to satisfy the is
operator, and if o
is indeed an Animal
, we make it check again to validate the cast.
It's more efficient to do this instead:
Animal a = o as Animal;
if(a != null)
a.Speak();
The as
operator is a cast that won't throw an exception if it fails, instead returning null
. This way, the CLR checks the object's type just once, and after that, we just need to do a null check, which is more efficient.
But beware: many people fall into a trap with as
. Because it doesn't throw exceptions, some people think of it as a "safe" cast, and they use it exclusively, shunning regular casts. This leads to errors like this:
(o as Animal).Speak();
In this case, the developer is clearly assuming that o
will always be an Animal
, and as long as their assumption is correct, everything works fine. But if they're wrong, then what they end up with here is a NullReferenceException
. With a regular cast, they would have gotten an InvalidCastException
instead, which would have more correctly identified the problem.
Sometimes, this bug can be hard to find:
class Foo{
readonly Animal animal;
public Foo(object o){
animal = o as Animal;
}
public void Interact(){
animal.Speak();
}
}
This is another case where the developer is clearly expecting o
to be an Animal
every time, but this isn't obvious in the constructor, where the as
cast is used. It's not obvious until you get to the Interact
method, where the animal
field is expected to be positively assigned. In this case, not only do you end up with a misleading exception, but it isn't thrown until potentially much later than when the actual error occurred.
In summary:
If you only need to know whether or not an object is of some type, use is
.
If you need to treat an object as an instance of a certain type, but you don't know for sure that the object will be of that type, use as
and check for null
.
If you need to treat an object as an instance of a certain type, and the object is supposed to be of that type, use a regular cast.
as
isn't really type checking though... – Carpas
is certainly a form of type-checking, every bit as much asis
is! It effectively usesis
behind the scenes, and is used all over the place in MSDN in places where it improves code cleanliness versusis
. Instead of checking foris
first, a call toas
establishes a typed variable that's ready for use: If it's null, respond appropriately; otherwise, proceed. Certainly something I've seen and used quite a bit. – Shearas
/is
(covered in https://mcmap.net/q/10248/-which-is-good-to-use-object-gettype-typeof-type-or-object-is-type-duplicate) assuming its semantic works for your case. – ExpatiateGetType
method you are linking to is inSystem.Reflection.Assembly
-- a completely different method and irrelevant here. – Herschelherself