Skip to content
Theory · Inheritance and interfaces · lesson 9 of 10

Type checks: is and as

The fleet's dispatcher has received an order: deliver some cargo. The list says only "vehicle number three." Before sending it out, the dispatcher needs to know whether it is a truck: a car will not carry cargo.

A program faces the same task. A variable of type Vehicle can lead to a truck, but you cannot call a truck's method through it. If v is declared as Vehicle v = new Truck();, the line v.Load(); does not compile:

TEXT
error CS1061: 'Vehicle' does not contain a definition for 'Load' and no accessible extension method 'Load' accepting a first argument of type 'Vehicle' could be found (are you missing a using directive or an assembly reference?)

The compiler knows only the variable's type, and Vehicle has no method Load. To reach it, you need a variable of type Truck. You can get one with a cast, the way numbers were converted with (int): Truck t = (Truck)v;. But if the object turns out not to be a truck, the program stops with an exception. That is why the type is checked first.

The is operator

C#
Vehicle v = new Truck();
Console.WriteLine(v is Truck);   // True
Console.WriteLine(v is Vehicle); // True
Console.WriteLine(v is Car);     // False
if (v is Truck t)
{
    t.Load(); // loading
}

class Vehicle { }
class Car : Vehicle { }
class Truck : Vehicle
{
    public void Load() => Console.WriteLine("loading");
}

Let's walk through it line by line:

  1. v is Truck checks the object's real type, and the line prints True: the object is a truck.
  2. The line with v is Vehicle prints True as well: a truck is a kind of vehicle, and is takes base classes into account too.
  3. The line with v is Car prints False: a truck is not a car.
  4. v is Truck t makes the same check and, if it passes, puts a reference to the object into a new variable t of type Truck. Inside the if, you can call Load through t.

If the variable is null, is gives false for any type: there is no object, so there is nothing to check.

The as operator

as tries to convert a reference to a type. If the object fits, the result is a reference of that type. If it does not, the result is null, and no exception happens:

C#
Vehicle w = new Car();
Truck? none = w as Truck;
Console.WriteLine(none == null); // True
Truck? found = v as Truck;
if (found != null)
{
    found.Load(); // loading
}

This fragment continues the lesson's first program:

  1. w as Truck gives null: w leads to a car.
  2. v as Truck gives a reference to the truck. After the check for null, Load is called through it.

Since as can return null, the variable that receives its result is declared with a nullable type (here Truck?) and checked before use.

as works only with types that allow null. An int cannot hold null, so as has no way to say "it did not work." So with object o = 5;, the line int n = o as int; does not compile:

TEXT
error CS0077: The as operator must be used with a reference type or nullable type ('int' is a non-nullable value type)

With the type int?, the same check compiles: int? n = o as int?;.

Here the dispatcher analogy is off. A dispatcher can be wrong when looking at a vehicle from far away. The is check is never wrong: the real type is stored in the object itself, and the runtime reads it from there.

A cast, is or as

Interviewers often ask you to compare three ways to reach the truck through the variable v of type Vehicle:

CodeThe object is a TruckThe object has another type
(Truck)va reference of type Truckan InvalidCastException
v is Truck ttrue, the reference in tfalse
v as Trucka reference of type Trucknull

is with a variable fits when an action follows the check right away. as fits when what you need next is a reference or null. The cast (Truck)v is written only when the type is known for sure.

Common mistake

The type was cast without a check:

C#
Vehicle other = new Car();
Truck bad = (Truck)other; // ✗
bad.Load();
TEXT
Unhandled exception. System.InvalidCastException: Unable to cast object of type 'Car' to type 'Truck'.

The program compiles: the compiler does not know which object other will lead to at run time. On the line with the cast, the program stops with an InvalidCastException, and Load never runs. Check the type with is or as before calling a truck's methods.

Key points

  • v is Truck checks the object's real type, base classes included, and v is Truck t also gives you a variable of that type.
  • v as Truck returns a reference or null; with int it does not compile, while with int? it works.
  • Casting with (Truck)v to an unsuitable type stops the program with an InvalidCastException.
  • For a variable that is null, the is check gives false.