C# 7.0: Small but welcome improvements

C# 7.0: Small but welcome improvements

I know I said the next post would be about benchmarking, and it was fully my intention, but then the real world happens. Recently, Microsoft announced the tentative feature-set of C# 7.0. Glancing over it, it looks like a similar improvement to what C# 6.0 brought us; it brought us 12 relatively minor improvements, while C# 7.0 clocks in at around 10 improvements. Let’s look at the ones that stand out.

Out variables

The gist of it is that instead of writing this:

string result;
if (dictionary.TryGetValue(key, out result))
    // it worked!

We can now write this:

if (dictionary.TryGetValue(key, out string result))
    // it worked!

It saves you an extra line of code and keeps you in the flow of typing, because you don’t have to jump back and add a variable. Time will tell if the time savings are worth the possible extra mental load.

Pattern matching

C# is adopting more and more functional paradigms, but that shouldn’t surprise you; it’s a shift that started with the introduction of LINQ. Pattern matching is one of those, and while it’s currently limited in scope, only supporting is and case, there are plans to expand this.

Both case and is pattern matching will increase the number of cases where you can join a variable’s declaration and initialization. For me, that’s less mental load, so it increases readability. It also makes writing code where you’d normally use an as clause a lot more enjoyable to write.

In a supreme case of tunnel vision, I spent an inordinate amount of time wondering if is also supports the when clause, you know, for ‘additional conditions’. I thought to myself: I must download the Visual Studio “15” Preview and find out. I gave Jean-Luc Picard a headache This is, of course, how you do ‘additional conditions’:

if ((shape is Rectangle s) && s.Width == s.Length)
   // it's a square!

Tuples

Alright, let’s move past the stupidity and onto the next productivity enhancer. I think tuples are a great solution for when you have to return a very small number of values from a private method, like a query that returns values from a number of related columns. When you construct a tuple with more than four values, I think it’s high time to introduce a DTO, because readability will most likely suffer otherwise.

private (string honorific, string first, string middle, string last, string postNominal) ParseName(string name)

My eyes hurt from reading this. Please, think of my eyes and introduce a DTO when you have too many values to return. What baffles me is that C# technically supports up to 8 (eight!) values in a single tuple. In my very humble opinion, if you use that many values, you need a stern talking to.

Deconstruction

Basically the consuming side’s counterpart of tuples, this feature’s main use case in my eyes is assigning values from a tuple to existing variables.

int x = 0, y = 0;
if (point != null)
    (x, y) = point;

Deconstructing tuples into new variables is only mildly useful, as you can just address the tuple’s members directly without having to create more variables (and possibly more mental load). Deconstructing arbitrary types is definitely only useful when using existing variables.

Local functions

A colleague of mine asked “Can’t you achieve the same thing with a lambda expression?” You cannot. Lambda expressions don’t have a name, so they can’t really self-reference. You can write a solution for that with delegates, but it’s pretty ugly:

void WriteFibonacciSequence()
{
    Func<int, int> fib = null;

    fib = i =>
    {
        if (i == 0)
            return 0;
        else if (i == 1)
            return 1;
        else
            return fib(i - 2) + fib(i - 1);
    };

    for (int i = 0; i < 15; i++)
    {
        Console.WriteLine(fib(i));
    }
}

This is pretty fragile; if someone modifies the value of fib (by accident, of course), your code is broken. Local functions don’t have this problem.

void WriteFibonacciSequence()
{
    int Fib(int i)
    {
        if (i == 0)
            return 0;
        else if (i == 1)
            return 1;
        else
            return Fib(i - 2) + Fib(i - 1);
    };

    for (int i = 0; i < 15; i++)
    {
        Console.WriteLine(Fib(i));
    }
}

Also, lambda expressions and anonymous delegates don’t support the yield return and yield break statements, while local functions do. Now you can finally stop writing separate private generator methods!

Literal improvements

Small stuff, but the addition of binary literals is nice and being able to insert _ as a separator at arbitrary positions might make some values more understandable.

Ref returns and locals

void Foo()
{
   int[] values = { 1, 2, 3, 4, 5, 6 };
   ref int bar = ref GetBar(values);
   bar = 42; // values is now { 1, 2, 3, 4, 42, 6 }
}

ref int GetBar(int[] values)
{
   return values[4];
}

This is the counterpart to ref method arguments, and it completes the full implementation of ‘references to value types’, and it’s probably most useful to high performance, memory critical applications.

Generalized async return types

This is another counterpart to a feature introduced in an earlier language version. In this case it’s async and await. In C# 6.0 you could already await things that are not Task; now you can also return things that are not Task from an async method.

This is a feature aimed at library developers, because it’s basically only plumbing to make sure you are able to return Foo from an async method, provided that you implement the async method builder and the awaitable.

More expression-bodied members

Expression-bodied members are wonderfully useful. The community rallied and implemented them in some of the places were Microsoft didn’t, which is awesome.

Throw expressions

Let’s look at one of the samples Microsoft provides:

public string Name { get; }
public Person(string name) => Name = name ?? throw new ArgumentNullException(nameof(name));

This is wonderfully specific and also doesn’t require any new methods or curly braces. I like it!

Conclusion

C# 7.0 doesn’t make as big a splash as C# 5.0 did, but it contains some very useful features that are bound to enhance your development productivity. I’m looking forward to using this in production code.

C# was already a mixed-paradigm language since the introduction of LINQ, and I’m happy to see Microsoft is continuing down that path. One of the features that didn’t make it which I would have loved to see is non-nullable reference types but that is understandably a much harder problem to solve.