Overview of new features in C# 7

With Visual Studio 2017 RC being already released, and the RTM being not too far off, it's time for a quick overview of the new language features. For the full list and a more in-depth explanation of the features mentioned in this post, check out What's new in C# 7 on MSDN.

This post is mainly a quick glance of what's to come, in no particular order. Some features are already present in C# 6 and have been extended, others are completely new.

A lot of this is syntactic sugar. I love syntactic sugar.

Out variables

Before, the out keyword was a bit of a hassle. Declare the variable, type it explicitly, and then write the call to the method that populates the variable. Fortunately, this changes in C# 7 - variables passed as out are automatically declared and can be re-used in the scope of the call:

if (TrySomething(out string result))
    Console.WriteLine(result);

The only caveat here is that you have to declare it as out T result, rather than just out result like before. I assume this is to maintain backward compatibility.

As the compiler can infer the type of result, using var is valid, too:

if (TrySomething(out var result))
    Console.WriteLine(result);

Pattern matching

Completely new to C# is the notion of patterns. Similar to the improvement of out described above, variables can be declared inside patterns and used within direct scope.

For instance, consider a method we can pass an object to, but depending on the type passed, we want to handle it differently. Before, we'd do something like:

string s = param as string;
if (s != null)
{
    // param is a string
}

Which is kind of ugly, and you'd need to do this for every single type that param could be. With pattern matching, you'd do something along the lines of:

if (param is int i)
{
    Console.WriteLine($"int: {i}");
    return;
}

if (param is string s)
{
    Console.WriteLine($"string: {s}");
    return;
}

// Handle it some other way
Broader switch statements

Continuing the patterns described above, you can now switch on more than just primitive types, and you can use patterns in case clauses. Using this, the previous code sample can be rewritten to:

switch (param)
{
    case int i:
        Console.WriteLine($"int: {i}");
        break;
    case string s:
        Console.WriteLine($"string: {s}");
        break;
    default:
        Console.WriteLine($"other: {param}");
        break;
}

Now I'm not entirely sure I've ran into a case where I needed code like this before, but I'm sure it'll come in handy when you are working on an existing code base that doesn't take strong typing too seriously and passes everything as an object.

Multiple return values

Or Tuples. Tuples have been in the BCL since forever, but in C# 7 they are used to support multiple return values, syntactically much like Python has done it for a while now.

These values are returned as a ValueTuple under the hood, and can be either anonymous (Item1, Item2, etc), or named. For instance:

private (string s, int i, bool b) Something() => ("s", 10, true);

Returns a ValueTuple<string, int bool>, with elements named s, i and b. You'd call this method like var val = Something();, and you can access the return values by their name as returned by the method, so val.s, but also simply as val.Item1, since they're still really tuples under the hood - IntelliSense simply shows you their names if they are defined.

This eliminates the need for types that simply exist for a very short period of time to aggregate some values, like a LoginResult type in an authentication service that would contain a session ID, time stamp and perhaps a token. You can now simply return all three those values from the method directly.

Deconstruction

Similarly, you can now deconstruct types into several values by declaring a deconstructor in a type. Tuples have a deconstructor pre-defined, but you can define them in any custom type as well.

For example, taking the LoginResult discussed before that contains a bool indicating success and a string identifying the user, we can declare a deconstructor like so:

public void Deconstruct(out bool isSuccessful, out string userId)
{
    isSuccessful = IsSuccessful;
    userId = UserId;
}

And grab the values from a login result like this:

(bool success, string userId) = Login(username, password);

We can now use success and userId as variables directly in the current scope. Something to notice here is that constructors and deconstructors (as the name implies) are effectively opposites of each other. Where a constructor usually takes arguments and assigns them to its members, a deconstructor takes out params, and assigns members values to those arguments.

Initially, you'd be able to define wildcards (*) for values you didn't care about, but at least in the RC, that part of the feature doesn't appear to have made the cut.

Local functions

Previously achievable through the use of Func<T>, you can now declare methods inside methods. For example, given a collection of objects that you need to verify are valid, you could write something like this in a single method:

Again, you could do this previously by using a Func<AuthenticationResult, bool> with virtually the same code. The only difference is that for local functions, they can be declared after they're used, even after a return statement. At least as far as I can tell.

The compiler will most likely generate different code for a local function than a Func<T, T2> though, but I haven't dived into it that deep yet, and probably won't until all this is set in stone and actually released.

Ref returns

This one's fairly huge. We could pass by reference before, but now we can also return by reference instead of just by value.

Given an array of ints, where we could only look up an item in the array and return its value, we can now look it up and return a reference to that item in the array, allowing callers of our method to modify the item later on:

public int[] _numbers = Enumerable.Range(0, 100).ToArray();

public ref int FindNumber(int idx)
{
    return ref _numbers[idx];
}

public void Foo()
{
    ref int num = ref FindNumber(20);

    num = 40; // _numbers[20] is now 40
}

That's a lot of ref. Declaring and returning by ref is very, very explicit because the behaviour is so different from the normal, non-ref behaviour.

Expression bodies for everything

Well, not everything, but more things at least. You can now use expression bodies for property getters and setters, ctors, finalizers, and probably a thing or two more.

Nothing huge, but definitely a welcome improvement.

Throwing in expressions

Again nothing really huge, but one I'll definitely love. A lot of my constructors for example use a class called Requires, which contains a bunch of methods that are exclusively used for precondition checks. You end up checking for null on a value, and if it's not null, assign it to a member:

public JwtTokenProvider(TokenProviderOptions providerOptions)
{
    Requires.NotNull(providerOptions, nameof(providerOptions));

    _options = providerOptions;
}

The above can now be replaced with:

public JwtTokenProvider(TokenProviderOptions providerOptions) => 
    _options = providerOptions ?? throw new ArgumentNullException(nameof(providerOptions));

Fewer lines of code is better. This is better. It also makes it much more verbose that this code is throwing an ArgumentNullException than a call to Requires.NotNull does - you no longer have to look at the implementation of NotNull to figure out that it actually throws.

Conclusion

There's a bunch of good stuff coming up shortly in the next iteration of C#. Though I'm not as hyped as I was for the inclusion of async and await, I can definitely see scenarios where ref returns result in much cleaner code than before.

As a programmer, I'm very happy Microsoft is both iterating on the language periodically, and is implementing features that for the most part, make sense.

I've left two new features out of this post: literal improvements and broader async return types. Reason being that while binary literals, number separators and ValueTask or custom Task types are kind of neat, I think their use cases are extremely limited to the point where you'll probably use them as often as you use the fixed keyword. They're in the MSDN article linked at the top of this post though, if you really want to read up on them.

Bonus

If you currently have Visual Studio 2017 RC installed, try writing null is string in one of your code files. Make sure to save your work before you do, though.

Thanks for reading!