C# 7 Pattern Matching and Tuples Guide

C# 7 introduced significant enhancements to modern software development, most notably first-class support for tuples and foundational pattern matching features. These additions streamlined common programming tasks by simplifying how developers return multiple values from methods, inspect types, and branch logic conditionally. This article outlines the specific features introduced for both tuples and pattern matching in C# 7, detailing their syntax, capabilities, and practical benefits.

Tuples in C# 7

Prior to C# 7, returning multiple values required using out parameters, custom classes/structs, or the allocate-heavy System.Tuple. C# 7 introduced built-in language support for tuples backed by the lightweight System.ValueTuple value type.

ValueTuple and Tuple Syntax

Tuples in C# 7 are value types, which avoids heap allocation and improves performance. Developers can create and return tuples using concise, parenthesis-based syntax:

(double Latitude, double Longitude) GetCoordinates()
{
    return (37.7749, -122.4194);
}

var coords = GetCoordinates();
Console.WriteLine($"Lat: {coords.Latitude}, Long: {coords.Longitude}");

Tuple Deconstruction

C# 7 allows consumers to unpack the elements of a tuple directly into individual variables:

var (lat, lon) = GetCoordinates();

Developers can also define a Deconstruct method within any class or struct to enable tuple-like deconstruction syntax on custom types:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public void Deconstruct(out string first, out string last)
    {
        first = FirstName;
        last = LastName;
    }
}

// Usage:
var person = new Person { FirstName = "Jane", LastName = "Doe" };
var (first, last) = person;

Pattern Matching in C# 7

C# 7 introduced the first stage of pattern matching, allowing developers to test the shape and type of data cleanly without verbose casting.

Type Patterns with the is Expression

The is expression was enhanced to test an expression's type and assign it to a new local variable simultaneously, removing the need for separate as casting and null checking:

if (input is string text)
{
    Console.WriteLine($"String length: {text.Length}");
}

If input is a string, text is scoped to the if block, strongly typed, and safe to use.

Pattern Matching in switch Statements

The switch statement was expanded to support pattern matching across any data type, rather than being restricted to primitives and strings:

  • Type Patterns: Cases can match specific types and declare variables.
  • The when Clause: Cases can include boolean conditions (guards) to filter matches further.
  • Order-Dependent Evaluation: Unlike traditional switches, pattern-based case labels are evaluated sequentially, requiring more specific patterns to appear before broader ones.
switch (shape)
{
    case Circle c when c.Radius > 10:
        Console.WriteLine($"Large circle with radius {c.Radius}");
        break;
    case Circle c:
        Console.WriteLine($"Small circle with radius {c.Radius}");
        break;
    case Rectangle r:
        Console.WriteLine($"Rectangle with area {r.Width * r.Height}");
        break;
    case null:
        Console.WriteLine("Shape is null");
        break;
    default:
        Console.WriteLine("Unknown shape");
        break;
}

The var Pattern

C# 7 also introduced the var pattern, which matches any value (including null) and assigns it to a newly declared variable. This pattern is commonly used within when clauses to temporarily capture intermediate results.