In this blog post, you will learn about new features in C# 7.0 which will make coding easier, and the code itself more readable and maintainable. The biggest changes are related to tuple and pattern matching functionality. Also, there is a possibility of declaring local functions within an existing function, as well as a deconstruction of types into smaller parts and assigning their values to new variables. Digit separators and binary literals are introduced to make the code more transparent.

Pattern matching

Pattern matching is a new feature in C# 7.0 which is not yet complete but will be in the forthcoming versions. There are two ways of pattern matching, with is expression and switch statement. The class below is used in two examples to follow.

public class Foo
{
    public string A { get; set; }     
}

public class Bar : Foo
{
    public int B { get; set; }

    public Bar(string a, int b)
    {
        this.A = a;
        this.B = b;
    }
}

public class Baz : Foo
{
    public bool C { get; set; }

    public Baz(string a, bool c)
    {
        this.A = a;
        this.C = c;
    }
}

Is-expression pattern

In the code below, the is expression pattern checks if the object matches with the given type, and adds variable to the right side of the expression, so the result is cast into that variable.

Foo foo = new Foo();
Bar bar = new Bar("bar", 1);
Baz baz = new Baz("baz", true);
if (bar is Foo d)
    Console.WriteLine($"Bar is {d.A}");
else Console.WriteLine($"Bar is not Foo");
//OUT: Bar is bar

Switch statement pattern

The switch case statement in C# 7.0 is enhanced so it can be used on any type of variable (not only on primitive ones ). Case clauses can contain a pattern and can have additional conditions on them, as shown in the example below.

switch (baz)
{
    case Foo foo1 when (foo1.A == "bar"):
        Console.WriteLine($"Foo.A = {foo1.A}");
        break;
    case Baz baz2 when (baz2.A == "baz"):
        Console.WriteLine($"Baz.C = {baz2.C}");
        break;
    default:
        Console.WriteLine("Not found");
        break;
}
//OUT: Baz.C = True

Tuples

Tuples already exist in C# since .NET Framework 4.0, but C# 7.0 enhances the functionality and code readability by adding tuple types and tuple literals. In the example below the method can, at once, return multiple variables all wrapped up as elements in a tuple value. Those elements can be accessed individually.

public (string, string, long) TupleReturn(bool qux)
{
    string foo = string.Empty;
    string bar = string.Empty;
    long baz = 0;

    if (qux)
    {
        foo = "foo";
        bar = "bar";
        baz = 3;
    }

    return (foo, bar, baz);
}
TupleExample te = new TupleExample();
var foobar = te.TupleReturn(true);
Console.WriteLine($"Tuple returned values: foo = {foobar.Item1}, bar = {foobar.Item2}, baz = {foobar.Item3} ");
//OUTPUT: Tuple returned values: foo = foo, bar = bar, baz = 3

Default names are Item1, Item2, etc., but new ones can be assigned to tuple types as shown in the code below:

public (string foo, string bar, long baz) TupleReturn(bool qux)

...

Console.WriteLine($"Tuple returned values: foo = {foobar.foo}, bar = {foobar.bar}, baz = {foobar.baz} ");
//OUTPUT: Tuple returned values: foo = foo, bar = bar, baz = 3

Local functions

This feature allows defining and calling local methods within the other method. The local method can be defined in any scope, and it will be available in that scope and all inner scopes, same as the local variable. Here is an example of three different ways that can be used to define the local function:

static void Main(string[] args)
{
    int Foo(int foo)
    {
        return 100 * foo;
    }
    Console.WriteLine(Foo(5));
    //OUT: 500

    int Bar(int bar) => 100 * bar;
    Console.WriteLine(Bar(5));
    //OUT: 500

    int qux = 100;
    int Baz(int baz)
    {
        return qux * baz;
    }
    Console.WriteLine(Baz(5));
    //OUT: 500
}

Deconstruction

Deconstruction is a process used for splitting the variable value into parts and storing them into new variables. Deconstruction can be easily used and demonstrated on tuples like this:

TupleExample te = new TupleExample();
(string foo, string bar, long baz) = te.TupleReturn(true);
Console.WriteLine($"Tuple returned values: foo = {foo}, bar = {bar}, baz = {baz} ");
//OUTPUT: Tuple returned values: foo = foo, bar = bar, baz = 3

Deconstruction can be done in a few different ways:

1) using var keyword for the individual variables declaration

(var foo, var bar, var baz) = te.TupleReturn(true);

2) placing a single var keyword outside of the parentheses as an abbreviation

var (foo, bar, baz) = te.TupleReturn(true);

3) using existing variables with a deconstructing assignment

string foo = string.Empty;
string bar = string.Empty;
long baz = 0;
(foo, bar, baz) = te.TupleReturn(true);

Literal improvements


Digit separator

We can now use a digit separator "_" inside the number literals. In the older versions of C# when there was a number, e.g. 1000000000000000, it was hard to count how many zeroes it had. Now, with a digit separator "_", this number can be separated in any way that will make it more transparent. Here is an example:

var b = 1_000_000_000_000_000;
var c = 1_00000_00000_00000;
Console.WriteLine(b);
Console.WriteLine(c);
//OUT: 1000000000000000
//OUT: 1000000000000000

Obviously, the value of the literal is unaffected by the digit separator "_". It is here just to improve the readability.

Binary Literal

C# has already supported hexadecimal literals by using 0x, but in C# 7.0 there are binary literals available as well. The digit separator "_" can be used inside the binary literals. In the code below, bit patterns are directly specified using 0b instead of having to know hexadecimal notation by heart.

var b = 0b10010101;
var c = 0b1100_1110_0111;
Console.WriteLine(b);
Console.WriteLine(c);
//OUT: 149
//OUT: 3303

Conclusion

C# 7.0 brings a lot of new features that will be useful for coding. Features such as pattern matching and local functions could change how a lot of everyday common code is written. Not all features are covered in this post, as some of them are still not available in Preview - out variables, ref returns, generalized async return types, etc. For more information, please visit MSDN blog.

More articles

Your opinion matters!

comments powered by Disqus