Have you been using C# for years? If you haven't looked at C# 7 new features yet, its time to learn some cool new features. Let's discuss few top new features of C# 7 like out variables, Pattern Matching, Tuples, and Discards.

 

Out variables

Before C# 7.0, we had to declare out variable before passing it to a method. Not anymore, now you can declare out variable at the time of passing it to a method.

// Old Way
bool oldWay;
bool.TryParse("true", out oldWay);
Console.WriteLine(oldWay);

// New Way
bool.TryParse("true", out bool newWay);
Console.WriteLine(newWay);

 

Pattern Matching

Pattern matching is a common feature in functional languages, it was introduced with C# 7.0 release.

Pattern matching provides a way to test a value against a series of condition or shape, and extract information from the value when it has a matching shape. So what's new about it, we use "if" and "switch case"  to test values against types and then extract the information from the value if required? Pattern matching just provides more concise syntax for just that, we will look at new syntax how it enables readable, concise code.

 There are three types of patterns:

  • constant: tests input against constant
  • type: tests input against a type, if so, extracts the value of input into a variable
  • var: always matches, it just puts the value of input into a variable.

In C# 7.0, comes with enhanced is and case clause: is now supports pattern on the right-hand side, instead of just a type. the case now supports matching input value to patterns, not just constant values. 

 

If-expression with patterns:

public void IfWithPatterns(object o)
{
    // Constant pattern null
    if (o is null) Console.WriteLine("o is null");

    // Type pattern "int n"
    if (o is int n) Console.WriteLine($"o is int and has value: {n} "); 

    // var pattern
    if (o is var x) Console.WriteLine($"x: {x}");
}

 

Switch with patterns:

switch (animal)
{
    case Dog d:
        d.Bark();
        break;
    case Cat c when c.Age > 5:
        c.Meow();
        break;
    case Cat ct:
        ct.Jump();
        break;
    default:
        Console.WriteLine("Unknown animal");
        break;
}

  

Value Tuples or Named Tuples

Before C# 7, only unnamed tuple was available, i.e. you could not give names to tuple fields and use Item1, Item 2, and so on to refer elements. In C# 7 onwards, you can give meaningful names to tuple fields, which makes it a named tuple. Named tuple makes the code more readable because you can give some meaningful names to Tuple fields now.

Before C# 7:

Tuple<int, string, bool> unnamedTuple = new Tuple<int, string, bool>(25, "Scott", true);

Console.WriteLine($"Name: {unnamedTuple.Item2}, Age: {unnamedTuple.Item1}, IsAdmin: {unnamedTuple.Item3}");

C# 7:

(int Age, string Name, bool IsAdmin) namedTuple = (25, "Scott", true);
Console.WriteLine($"Name: {namedTuple.Age}, Age: {namedTuple.Age}, IsAdmin: {namedTuple.IsAdmin}");

// OR you can also specify names on right side of assignment

var namedTuple2 = (Age: 27, Name: "Kevin", IsAdmin: true);
Console.WriteLine($"Name: {namedTuple2.Age}, Age: {namedTuple2.Age}, IsAdmin: {namedTuple2.IsAdmin}");

  

Deconstruction

When you split Tuple or other types into its parts and assign those parts individually to fresh variables.

For example

(string name, int age, bool isAdmin) = GetInfo(id);
Console.WriteLine($"{name}, {age}, {isAdmin}");

You can also use var for individual variables declared:

(var name, var age, var isAdmin) = GetInfo(id);
Console.WriteLine($"{name}, {age}, {isAdmin}");

Or a single var outside parenthesis:

var (name, age, isAdmin) = GetInfo(id);
Console.WriteLine($"{name}, {age}, {isAdmin}");

 Deconstruction can work with types other than Tuple, as long as it has deconstructor method of the form:

public void Deconstruct(out T1 x1, ..., out Tn xn) { ... }

 

Discards

Discards are temporary, write-only variables used in assignments when you don't care about the value assigned. They are particularly useful when deconstructing tuples and user-defined types, as well as when calling methods with out parameters.

Sometimes you don't need values returned from a method, especially when deconstructing tuples and methods with out parameters, for example:

if(bool.TryParse(arg1, out bool value))
{
    // Logic here
}

Here we are not using parsed value so why to declare it, in C# 7 with the introduction of discards, you can solve this problem:

if(bool.TryParse(arg1, out bool _))
{
    // Logic here
}

 

Local functions

Sometimes a method is called from just one location. You can now declare such functions inside another method.

Usage:

public void ShowMessage(string message)
{
    string GetGreetings()
    {
        if (DateTime.Now.Hour < 12)
            return "Good Morning!";
        else if (DateTime.Now.Hour < 17)
            return "Good Afternoon";
        else
            return "Good Evening";
    }

    void ShowMessage()
    {
        Console.WriteLine($"{GetGreetings()}, {message}");
    }

    ShowMessage();
}

In example above, ShowMessage() and GetGreetings() are local functions.

 

Related post: C# 7.1 New Features

 

What do you think about the new features? Have any questions, suggestions? Drop a comment below!