C# has long supported two operators to check the type of an object: is and as. C# 7 adds a new way to use the is operator that combines is with basic patterns to provide an alternative for as.

The new is patterns provide a nicer syntax for safe casting than both the existing is and as operators and address the limitations of those operators as well.

TL;DR

C# 7 adds support for constant patterns, type patterns, and var patterns to the is operator. Use them like this:

if(input is null)
  return 0;

if(input is 5)
  return 5;

if(input is int count)
  return count;

if(input is string text)
  return text.length;

if(input is var output)
  return -1;

Unsafe Casting

You often need to cast an object to another type. You can do that directly using the cast operator, (string)input, but what if input is not a string? Boom! You'll get an exception.

If you're absolutely certain of the type of an object, you can get a tiny performance boost by using an unsafe cast. But because we want to avoid exceptions, it's better to use a safe cast.

Safe Casting with is

One way to cast safely is to check the type using is and then cast. The problem with this method is that input is accessed twice.

if(input is string)
{
  string text = (string)input;
}

Safe Casting with as

A better way to cast safely is to use the as operator, which returns null when input is not a string. This also avoids the small performance hit of accessing input twice.

string text = input as string;
if(text != null)
{
  ...
}

Problems with as

There are two limitations with the as operator.

  • It doesn't distinguish between a null value and the wrong type.
  • It doesn't work with non-nullable types like int.

Update (14th April 2017): As Yves Goergen notes in the comments, null has no type, so it is always the wrong type and in fact, is treats null in the same way, both with and without type patterns. Therefore, the first bullet point is not a limitation of as; instead, it is the cause of a problem I've encountered with the usage of as: the negation of as, if(text == null), is used when if(text == null && input != null) is intended. It seems a lot more common to use the negation of as incorrectly, than the negation of is, if(!(input is string)).

Safe Casting with is and type patterns

The new method of casting safely in C# 7 is to use is with type patterns. Here is an example of how to use is with type patterns to safely cast input to a string.

if(input is string text)
{
  ...
}

Not only is this the shortest and cleanest syntax, but it has none of the problems that plagued the previous methods:

  • input is only accessed once.
  • The pattern will not match if input is null.
  • Non-nullable types like int are supported.

Type Patterns and Constant Patterns

The last example used is to match on what is called a type pattern: string text. Type patterns do not match null values, because null is type-less. Therefore, in the previous example, text will never be null.

If we want to match null, we need to use a constant pattern. Constant patterns can be used with is to match any constant value including null. Here are three examples of constant patterns, followed by two examples of type patterns.

if(input is null)
  return 0;

if(input is 3)
  return 3;

if(input is "Hello")
  return 5;

if(input is int count)
  return count;

if(input is string text)
  return text.length;

Scope of Pattern Variables

When a pattern variable like text is introduced by a pattern match, it is introduced into the enclosing block's scope.

In if statements and other statements that do not establish their own scope, the pattern variable is available to later code in the same scope. This means they behave as though they were declared immediately prior to where they are used, like text in the earlier as example. This enables their use with negations:

if(!(input is string text))
  return;

Console.WriteLine(text.Length);

In while statements and other statements that establish their own scope, the pattern variable is only available within the newly established scope, i.e. inside the while loop.

object input = "hello";
while(input is string output)
{
    Console.WriteLine(output);

    if(input == "world")
        input = null;
    else
        input = "world";                
}

// output is no longer in scope

Var Pattern

There is one final pattern available: the var pattern. The var pattern always matches, always returns true, and it merely puts the value into a new variable with the same type as the input.

Unlike type patterns, the var pattern also matches null.

string text = null;

if(text is var temp)
    Console.WriteLine("true");

Output:
true

Avoiding Multiple Evaluations with the Var Pattern

You might ask: when would I ever use the var pattern? Isn't it absolutely useless? Well, Alexander Shvedov figured out that you can use it to avoid multiple evaluations as demonstrated in this gist.

Often you find yourself walking some hierarchy, a list, a tree, the DOM, until some condition is met. For example, you might walk up the type hierarchy to the root type (yes, this is silly; it always ends up at Object).

while(type.BaseType != null)
  type = type.BaseType;

While succinct, this is not efficient. We're evaluating BaseType twice per iteration instead of once. Imagine if BaseType was a really expensive method call, like a database call. We can make it more efficient by using a temporary variable, temp, to avoid the duplicate evaluation.

Type temp;
while((temp = type.BaseType) != null)
  type = temp;

The var pattern provides a different way to achieve the same thing.

while(type.BaseType is var temp && temp != null)
  type = temp;

In this example, the inline assignment is quite readable. But in general, I detest inline assignments vehemently as they regularly become unwieldy with more complex method calls and conditions. It quickly becomes difficult to identify where the assignment ends and the conditions begin. Therefore, I think the var pattern is more readable.

Of course, in this particular example, a type pattern would be the most succinct and readable.

while(type.BaseType is Type temp)
  type = temp;

Conclusion

C# 7 has added basic pattern matching to the is operator. This means you won't need as as often and your code will be slightly more readable.

When casting, it'll be easier to distinguish null values from type mismatches and it'll be easier to work with non-nullable types. You'll also be able to eliminate some nasty inline assignments by using the var pattern.