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:
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
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:
v is Truckchecks the object's real type, and the line printsTrue: the object is a truck.- The line with
v is VehicleprintsTrueas well: a truck is a kind of vehicle, andistakes base classes into account too. - The line with
v is CarprintsFalse: a truck is not a car. v is Truck tmakes the same check and, if it passes, puts a reference to the object into a new variabletof typeTruck. Inside theif, you can callLoadthrought.
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:
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:
w as Truckgivesnull:wleads to a car.v as Truckgives a reference to the truck. After the check fornull,Loadis 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:
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:
| Code | The object is a Truck | The object has another type |
|---|---|---|
(Truck)v | a reference of type Truck | an InvalidCastException |
v is Truck t | true, the reference in t | false |
v as Truck | a reference of type Truck | null |
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:
Vehicle other = new Car();
Truck bad = (Truck)other; // ✗
bad.Load();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 Truckchecks the object's real type, base classes included, andv is Truck talso gives you a variable of that type.v as Truckreturns a reference ornull; withintit does not compile, while withint?it works.- Casting with
(Truck)vto an unsuitable type stops the program with anInvalidCastException. - For a variable that is
null, theischeck givesfalse.