Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

A type pattern in C# is a pattern matching construct used to evaluate whether an expression’s runtime type is compatible with a specified target type. If the type check succeeds, the pattern evaluates to true. When combined with a variable designation, it forms a declaration pattern that binds the matched value to a newly declared local variable of the target type in a single, atomic operation.

Syntax

A true type pattern (introduced in C# 9.0) consists solely of the type name and is used within pattern contexts such as switch cases or logical patterns. When a variable name is appended, it becomes a declaration pattern.
// Type pattern in a switch context (C# 9.0+)
case Type:
Type =>

// Type pattern combined within a logical pattern
expression is Type and [pattern]

// Declaration pattern (Type pattern + variable declaration)
expression is Type variableName
Note: An expression of the exact form expression is Type (without a variable or logical combinator) is parsed by the compiler as a legacy C# 1.0 is-type expression for backward compatibility, rather than a C# 9.0 type pattern.

Mechanics and Evaluation Rules

  • Type Compatibility: The pattern evaluates to true if the runtime type of the expression is exactly the target type, derives from the target type, or implements the target interface. Unlike the as operator, which only works with reference types and nullable value types, type patterns natively handle unboxing conversions for non-nullable value types.
  • Null Safety: An expression that evaluates to null will strictly evaluate to false against any type pattern. Type patterns inherently guarantee that the matched expression is not null.
  • Nullable Value Types: Nullable value types (T?) are strictly prohibited from being used as target types in patterns. Attempting to use a nullable value type in a pattern (e.g., obj switch { int? => ... } or expression is int? variableName) results in compiler error CS8116. Pattern matching evaluates the dynamic type of references or boxed values. While Nullable<T> is a standard, unboxed struct on the stack, boxing it invokes specific CLR behavior: it is never boxed as a Nullable<T>. Instead, it either becomes a null reference (if HasValue is false) or is boxed as the underlying non-nullable type T (if HasValue is true). To match a boxed nullable value type, the pattern must use the underlying type T.
  • Definite Assignment: When a variable is declared within a declaration pattern, C#‘s compiler flow analysis ensures the variable is only considered “definitely assigned” in code paths where the pattern explicitly evaluated to true.

Structural Implementations

The is Operator When used with the is operator, patterns act as boolean expressions. To use a C# 9.0 type pattern without a variable designation, it must be part of a logical pattern.
object obj = 42;

// Declaration pattern: Evaluates type and binds to 'i'
if (obj is int i)
{
    // 'i' is definitely assigned and strongly typed as int in this scope
    Console.WriteLine(i * 2);
}

// Type pattern: Evaluates type compatibility within a logical pattern (C# 9.0+)
if (obj is int and not 0)
{
    // Executes if obj is an unboxed integer that is not zero
}
Switch Statements Type patterns serve as case labels within switch statements, allowing polymorphic routing based on runtime types.
object data = "System String";

switch (data)
{
    case int i:
        // Declaration pattern: Matches if data is an unboxed int, binds to 'i'
        break;
    case IEnumerable<string> list:
        // Declaration pattern: Matches if data implements IEnumerable<string>, binds to 'list'
        break;
    case string:
        // Type pattern: Matches string, no variable binding (C# 9.0+)
        break;
}
Switch Expressions In C# 8.0+, type patterns form the arms of a switch expression, mapping types directly to evaluated return values.
object input = 5.5;

Type resultType = input switch
{
    int _ => typeof(int),       // Declaration pattern using a discard for the variable name
    double d => typeof(double), // Declaration pattern with variable binding
    string => typeof(string),   // Type pattern (C# 9.0+)
    _ => typeof(object)         // Discard pattern
};
Master C# with Deep Grasping Methodology!Learn More