What's New in C# 7.0?

A guide to the major new features introduced in C# 7.0, including tuples and deconstruction, pattern matching, and local functions. Learn how these features can make your C# code more concise and expressive.

C# 7.0, released in early 2017, was a significant release that brought a host of powerful new features to the language. The theme of this release was making code more concise and data manipulation easier. Several features introduced in C# 7.0 have become fundamental to modern C# programming.

Let's explore the most impactful new features.

1. Tuples and Deconstruction

This is arguably the most significant feature of C# 7.0. For years, if you wanted to return multiple values from a method, you had to use out parameters or create a custom class. C# 7.0 introduces a new, lightweight syntax for tuples.

// A method that returns a tuple with named elements
(string, string, int) GetPerson()
{
    return ("Alice", "Smith", 30);
}

// You can consume the tuple like this:
var person = GetPerson();
Console.WriteLine($"{person.Item1} is {person.Item3} years old.");

Even better, you can deconstruct a tuple into separate variables.

// Deconstruct the tuple into individual variables
(string firstName, string lastName, int age) = GetPerson();
Console.WriteLine($"{firstName} is {age} years old.");

This provides a clean and simple way to work with multiple return values.

2. Pattern Matching

Pattern matching introduces a new way to express control flow in your code based on the "shape" of your data. C# 7.0 introduces two new patterns.

is expressions with patterns:

The is operator is extended to test a variable's type and, if the test is successful, assign it to a new variable of that type.

public void PrintInfo(object obj)
{
    if (obj is int i)
    {
        Console.WriteLine($"It's an integer: {i}");
    }
    else if (obj is string s)
    {
        Console.WriteLine($"It's a string: {s.ToUpper()}");
    }
}

switch statements with patterns:

The switch statement is also enhanced to support patterns, allowing you to switch on the type of a variable.

switch (shape)
{
    case Circle c:
        Console.WriteLine($"Circle with radius {c.Radius}");
        break;
    case Rectangle r when (r.Width == r.Height):
        Console.WriteLine($"It's a square!");
        break;
    case Rectangle r:
        Console.WriteLine($"Rectangle with width {r.Width}");
        break;
    case null:
        // Handle null case
        break;
    default:
        // Handle default case
        break;
}

Notice the when clause, which allows you to add an additional condition to a case.

3. Local Functions

You can now declare a function inside another method. This is useful for creating small helper functions that are only used by the containing method and don't pollute the scope of your class.

public int CalculateFactorial(int n)
{
    // Define a local function
    int Factorial(int number)
    {
        if (number < 0) throw new ArgumentException("Negative number");
        if (number == 0) return 1;
        return number * Factorial(number - 1);
    }

    // Call the local function
    return Factorial(n);
}

4. out Variables

A small but very convenient improvement. You can now declare the variable for an out parameter right at the point where it is passed as an argument, instead of having to declare it on a separate line.

The Old Way:

int number;
if (int.TryParse("123", out number))
{
    // ...
}

The New Way:

if (int.TryParse("123", out int number))
{
    // You can use 'number' here
}

Conclusion

C# 7.0 was a feature-packed release that significantly improved the expressiveness of the language. Tuples and pattern matching, in particular, have become fundamental tools for writing cleaner, more readable, and more robust C# code. These features laid the groundwork for even more powerful patterns that would be introduced in later versions of the language.