Checks if an object is compatible with a given type, or (starting with C# 7) tests an expression against a pattern.
Testing for type compatibility
The is
keyword evaluates type compatibility at runtime. It determines whether an object instance or the result of an expression can be converted to a specified type. It has the syntax
expr is type
where expr is an expression that evaluates to an instance of some type, and type is the name of the type to which the result of expr is to be converted. The is
statement is true
if expr is non-null and the object that results from evaluating the expression can be converted to type; otherwise, it returns false
.
For example, the following code determines if obj
can be cast to an instance of the Person
type:
if (obj is Person) {
// Do something if obj is a Person.
}
The is
statement is true if:
expr is an instance of the same type as type.
expr is an instance of a type that derives from type. In other words, the result of expr can be upcast to an instance of type.
expr has a compile-time type that is a base class of type, and expr has a runtime type that is type or is derived from type. The compile-time type of a variable is the variable's type as defined in its declaration. The runtime type of a variable is the type of the instance that is assigned to that variable.
expr is an instance of a type that implements the type interface.
The following example shows that the is
expression evaluates to true
for each of these conversions.
using System;
public class Class1 : IFormatProvider
{
public object GetFormat(Type t)
{
if (t.Equals(this.GetType()))
return this;
return null;
}
}
public class Class2 : Class1
{
public int Value { get; set; }
}
public class Example
{
public static void Main()
{
var cl1 = new Class1();
Console.WriteLine(cl1 is IFormatProvider);
Console.WriteLine(cl1 is Object);
Console.WriteLine(cl1 is Class1);
Console.WriteLine(cl1 is Class2);
Console.WriteLine();
var cl2 = new Class2();
Console.WriteLine(cl2 is IFormatProvider);
Console.WriteLine(cl2 is Class2);
Console.WriteLine(cl2 is Class1);
Console.WriteLine();
Class1 cl = cl2;
Console.WriteLine(cl is Class1);
Console.WriteLine(cl is Class2);
}
}
// The example displays the following output:
// True
// True
// True
// False
//
// True
// True
// True
//
// True
// True
The is
keyword generates a compile-time warning if the expression is known to always be either true
or false
. It only considers reference conversions, boxing conversions, and unboxing conversions; it does not consider user-defined conversions or conversions defined by a type's implicit and explicit operators. The following example generates warnings because the result of the conversion is known at compile-time. Note that the is
expression for conversions from int
to long
and double
return false, since these conversions are handled by the implicit operator.
Console.WriteLine(3 is int);
Console.WriteLine();
int value = 6;
Console.WriteLine(value is long);
Console.WriteLine(value is double);
Console.WriteLine(value is object);
Console.WriteLine(value is ValueType);
Console.WriteLine(value is int);
// Compilation generates the following compiler warnings:
// is2.cs(8,25): warning CS0183: The given expression is always of the provided ('int') type
// is2.cs(12,25): warning CS0184: The given expression is never of the provided ('long') type
// is2.cs(13,25): warning CS0184: The given expression is never of the provided ('double') type
// is2.cs(14,25): warning CS0183: The given expression is always of the provided ('object') type
// is2.cs(15,25): warning CS0183: The given expression is always of the provided ('ValueType') type
// is2.cs(16,25): warning CS0183: The given expression is always of the provided ('int') type
expr
can be any expression that returns a value, with the exception of anonymous methods and lambda expressions. The following example uses is
to evaluate the return value of a method call.
using System;
public class Example
{
public static void Main()
{
double number1 = 12.63;
if (Math.Ceiling(number1) is double)
Console.WriteLine("The expression returns a double.");
else if (Math.Ceiling(number1) is decimal)
Console.WriteLine("The expression returns a decimal.");
decimal number2 = 12.63m;
if (Math.Ceiling(number2) is double)
Console.WriteLine("The expression returns a double.");
else if (Math.Ceiling(number2) is decimal)
Console.WriteLine("The expression returns a decimal.");
}
}
// The example displays the following output:
// The expression returns a double.
// The expression returns a decimal.
Starting with C# 7, you can use pattern matching with the type pattern to write more concise code that uses the is
statement.
Pattern matching with is
Starting with C# 7, the is
and switch statements support pattern matching. The is
keyword supports the following patterns:
Type pattern, which tests whether an expression can be converted to a specified type and, if it can be, casts it to a variable of that type.
Constant pattern, which tests whether an expression evaluates to a specified constant value.
var pattern, a match that always succeeds and binds the value of an expression to a new local variable.
Type pattern
When using the type pattern to perform pattern matching, is
tests whether an expression can be converted to a specified type and, if it can be, casts it to a variable of that type. It is a straightforward extension of the is
statement that enables concise type evaluation and conversion. The general form of the is
type pattern is:
expr is type varname
where expr is an expression that evaluates to an instance of some type, type is the name of the type to which the result of expr is to be converted, and varname is the object to which the result of expr is converted if the is
test is true
.
The is
expression is true
if any of the following is true:
expr is an instance of the same type as type.
expr is an instance of a type that derives from type. In other words, the result of expr can be upcast to an instance of type.
expr has a compile-time type that is a base class of type, and expr has a runtime type that is type or is derived from type. The compile-time type of a variable is the variable's type as defined in its declaration. The runtime type of a variable is the type of the instance that is assigned to that variable.
expr is an instance of a type that implements the type interface.
If exp is true
and is
is used with an if
statement, varname is assigned and has local scope within the if
statement only.
The following example uses the is
type pattern to provide the implementation of a type's System.IComparable.CompareTo(Object) method.
using System;
public class Employee : IComparable
{
public String Name { get; set; }
public int Id { get; set; }
public int CompareTo(Object o)
{
if (o is Employee e)
{
return Name.CompareTo(e.Name);
}
throw new ArgumentException("o is not an Employee object.");
}
}
Without pattern matching, this code might be written as follows. The use of type pattern matching produces more compact, readable code by eliminating the need to test whether the result of a conversion is a null
.
using System;
public class Employee : IComparable
{
public String Name { get; set; }
public int Id { get; set; }
public int CompareTo(Object o)
{
var e = o as Employee;
if (o == null)
{
throw new ArgumentException("o is not an Employee object.");
}
return Name.CompareTo(e.Name);
}
}
The is
type pattern also produces more compact code when determining the type of a value type. The following example uses the is
type pattern to determine whether an object is a Person
or a Dog
instance before displaying the value of an appropriate property.
using System;
public class Example
{
public static void Main()
{
Object o = new Person("Jane");
ShowValue(o);
o = new Dog("Alaskan Malamute");
ShowValue(o);
}
public static void ShowValue(object o)
{
if (o is Person p) {
Console.WriteLine(p.Name);
}
else if (o is Dog d) {
Console.WriteLine(d.Breed);
}
}
}
public struct Person
{
public string Name { get; set; }
public Person(string name) : this()
{
Name = name;
}
}
public struct Dog
{
public string Breed { get; set; }
public Dog(string breedName) : this()
{
Breed = breedName;
}
}
// The example displays the following output:
// Jane
// Alaskan Malamute
The equivalent code without pattern matching requires a separate assignment that includes an explicit cast.
using System;
public class Example
{
public static void Main()
{
Object o = new Person("Jane");
ShowValue(o);
o = new Dog("Alaskan Malamute");
ShowValue(o);
}
public static void ShowValue(object o)
{
if (o is Person) {
Person p = (Person) o;
Console.WriteLine(p.Name);
}
else if (o is Dog) {
Dog d = (Dog) o;
Console.WriteLine(d.Breed);
}
}
}
public struct Person
{
public string Name { get; set; }
public Person(string name) : this()
{
Name = name;
}
}
public struct Dog
{
public string Breed { get; set; }
public Dog(string breedName) : this()
{
Breed = breedName;
}
}
// The example displays the following output:
// Jane
// Alaskan Malamute
Constant pattern
When performing pattern matching with the constant pattern, is
tests whether an expression equals a specified constant. In C# 6 and earlier versions, the constant pattern is supported by the switch statement. Starting with C# 7, it is supported by the is
statement as well. Its syntax is:
expr is constant
where expr is the expression to evaluate, and constant is the value to test for. constant can be any of the following constant expressions:
A literal value.
The name of a declared
const
variable.An enumeration constant.
The constant expression is evaluated as follows:
If expr and constant are integral types, the C# equality operator determines whether the expression returns
true
(that is, whetherexpr == constant
).Otherwise, the value of the expression is determined by a call to the static Object.Equals(expr, constant) method.
The following example combines the type and constant patterns to test whether an object is a Dice
instance and, if it is, to determine whether the value of a dice roll is 6.
using System;
public class Dice
{
Random rnd = new Random();
public Dice()
{
}
public int Roll()
{
return rnd.Next(1, 7);
}
}
class Program
{
static void Main(string[] args)
{
var d1 = new Dice();
ShowValue(d1);
}
private static void ShowValue(object o)
{
const int HIGH_ROLL = 6;
if (o is Dice d && d.Roll() is HIGH_ROLL)
Console.WriteLine($"The value is {HIGH_ROLL}!");
else
Console.WriteLine($"The dice roll is not a {HIGH_ROLL}!");
}
}
// The example displays output like the following:
// The value is 6!
var pattern
A pattern match with the var pattern always succeeds. Its syntax is
expr is var varname
where the value of expr is always assigned to a local variable named varname. varname is a static variable of the same type as expr. The following example uses the var pattern to assign an expression to a variable named obj
. It then displays the value and the type of obj
.
using System;
class Program
{
static void Main()
{
object[] items = { new Book("The Tempest"), new Person("John") };
foreach (var item in items) {
if (item is var obj)
Console.WriteLine($"Type: {obj.GetType().Name}, Value: {obj}");
}
}
}
class Book
{
public Book(string title)
{
Title = title;
}
public string Title { get; set; }
public override string ToString()
{
return Title;
}
}
class Person
{
public Person(string name)
{
Name = name;
}
public string Name
{ get; set; }
public override string ToString()
{
return Name;
}
}
// The example displays the following output:
// Type: Book, Value: The Tempest
// Type: Person, Value: John
Note that if expr is null
, the is
expression still is true and assigns null
to varname.
C# Language Specification
For more information, see the C# Language Specification. The language specification is the definitive source for C# syntax and usage.