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 positional pattern is a pattern matching construct in C# that deconstructs an expression into its constituent parts and applies nested patterns to those resulting values based on their ordinal position. It relies on the presence of a compatible Deconstruct method or inherent tuple semantics to extract values for evaluation.

Syntax

The pattern is defined by enclosing a comma-separated list of nested patterns within parentheses. A type declaration can optionally precede the parentheses to enforce a type check prior to deconstruction.
// General syntax
[Type] (pattern1, pattern2, ...)

Mechanics of Deconstruction

For a positional pattern to evaluate a custom type, the type must implement a Deconstruct method (either as an instance method or an extension method). The method must return void and declare out parameters corresponding to the values being extracted. A type can overload the Deconstruct method with different numbers of out parameters. During pattern matching, the compiler automatically resolves the positional pattern to the specific Deconstruct overload that matches the exact number of provided nested patterns.
public class Point
{
    public int X { get; }
    public int Y { get; }

    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }

    // Resolved when the pattern contains exactly two nested patterns
    public void Deconstruct(out int x, out int y)
    {
        x = X;
        y = Y;
    }

    // Resolved when the pattern contains exactly three nested patterns
    public void Deconstruct(out int x, out int y, out double magnitude)
    {
        x = X;
        y = Y;
        magnitude = Math.Sqrt(x * x + y * y);
    }
}
When the pattern is evaluated, the compiler invokes the matched Deconstruct method, binds the out parameters to temporary variables, and applies the nested patterns to those variables in the exact order they are declared.

Positional Records

In modern C#, positional record types are the primary and most idiomatic targets for positional patterns. When a positional record is defined, the compiler automatically generates a Deconstruct method behind the scenes that corresponds to the primary constructor parameters.
public record Employee(string Name, string Department, int Age);

object obj = new Employee("Alice", "Engineering", 28);

// Automatically utilizes the compiler-generated Deconstruct(out string, out string, out int)
bool isMatch = obj is Employee ("Alice", _, >= 25);

Pattern Application

Positional patterns support nesting any other valid C# pattern (e.g., constant, relational, property, or discard patterns) at each ordinal position.
object obj = new Point(5, 10);

bool isMatch = obj is Point (5, >= 10);
During the evaluation of the code above:
  1. The compiler checks if obj is of type Point.
  2. If true, it invokes Point.Deconstruct(out int x, out int y).
  3. It applies the constant pattern 5 to the first positional value (x).
  4. It applies the relational pattern >= 10 to the second positional value (y).

Variable Declaration

Positional patterns can declare new variables to capture the deconstructed values. This is achieved using the declaration pattern within the positional slots.
if (obj is Point (var xVal, int yVal))
{
    // xVal and yVal are now in scope and strongly typed
}

Omitting the Type Declaration

The explicit type declaration preceding the parentheses can be omitted for any type (including custom classes, records, and tuples) as long as the static type of the expression being evaluated is already known at compile time and provides a compatible Deconstruct method.
Point p = new Point(5, 10);
(int, string) data = (42, "Active");

// Type name omitted because the static type of 'p' is known
bool isPointMatch = p is (5, >= 10);

// Type name omitted because the static type of 'data' is known
bool isTupleMatch = data is (42, { Length: > 0 });

Discards

If a specific ordinal position is irrelevant to the match, the discard pattern (_) can be used to ignore that specific out parameter during deconstruction. This prevents unnecessary variable allocation and clarifies intent.
// Evaluates only the second positional value, ignoring the first
bool match = p is (_, 10);
Master C# with Deep Grasping Methodology!Learn More