is (C# Reference)

The is operator checks if the result of an expression is compatible with a given type, or (starting with C# 7.0) tests an expression against a pattern. For information about the type-testing is operator see the is operator section of the Type-testing and cast operators article.

Pattern matching with is

Starting with C# 7.0, 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's 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 expr isn't null, and 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.

Beginning with C# 7.1, expr may have a compile-time type defined by a generic type parameter and its constraints.

If expr is true and is is used with an if statement, varname is assigned within the if statement only. The scope of varname is from the is expression to the end of the block enclosing the if statement. Using varname in any other location generates a compile-time error for use of a variable that has not been assigned.

The following example uses the is type pattern to provide the implementation of a type's 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 (e == 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.0, it's 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, whether expr == 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!

Checking for null can be performed using the constant pattern. The null keyword is supported by the is statement. Its syntax is:

   expr is null

The following example shows a comparison of null checks:

using System;

class Program
{
    static void Main(string[] args)
    {
        object o = null;

        if (o is null)
        {
            Console.WriteLine("o does not have a value");
        }
        else
        {
            Console.WriteLine($"o is {o}");
        }

        int? x = 10;

        if (x is null)
        {
            Console.WriteLine("x does not have a value");
        }
        else
        {
            Console.WriteLine($"x is {x.Value}");
        }

        // 'null' check comparison
        Console.WriteLine($"'is' constant pattern 'null' check result : { o is null }");
        Console.WriteLine($"object.ReferenceEquals 'null' check result : { object.ReferenceEquals(o, null) }");
        Console.WriteLine($"Equality operator (==) 'null' check result : { o == null }");
    }

    // The example displays the following output:
    // o does not have a value
    // x is 10
    // 'is' constant pattern 'null' check result : True
    // object.ReferenceEquals 'null' check result : True
    // Equality operator (==) 'null' check result : True
}

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 variable of the same type as the compile-time type of expr.

If expr evaluates to null, the is expression produces true and assigns null to varname. The var pattern is one of the few uses of is that produces true for a null value.

You can use the var pattern to create a temporary variable within a Boolean expression, as the following example shows:

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        int[] testSet = { 100271, 234335, 342439, 999683 };

        var primes = testSet.Where(n => Factor(n).ToList() is var factors
                                    && factors.Count == 2
                                    && factors.Contains(1)
                                    && factors.Contains(n));

        foreach (int prime in primes)
        {
            Console.WriteLine($"Found prime: {prime}");
        }
    }

    static IEnumerable<int> Factor(int number)
    {
        int max = (int)Math.Sqrt(number);
        for (int i = 1; i <= max; i++)
        {
            if (number % i == 0)
            {
                yield return i;
                if (i != number / i)
                {
                    yield return number / i;
                }
            }
        }
    }
}
// The example displays the following output:
//       Found prime: 100271
//       Found prime: 999683

In the preceding example, the temporary variable is used to store the result of an expensive operation. The variable can then be used multiple times.

C# language specification

For more information, see The is operator section of the C# language specification and the following C# language proposals:

See also