State of the union: ReSharper C# 7 and VB.NET 15 support

Posted on by

When we released ReSharper 2016.3 (and earlier this week, ReSharper 2017.1 EAP), we introduced initial support for C# 7 and VB.NET 15. For example, ReSharper now understands binary literals and digit separators, as well as local functions with several inspections. We’re far from feature complete, but want to look ahead at what’s coming in terms of new programming language features and what we are working on for ReSharper (including some of the challenges building out those features). Here goes!

In this post:

Digit separators and binary literals

Both C# 7 and VB.NET 15 allow _ to occur as a digit separator inside numeric literals. This helps improve code readability. For example, adding digit separators in the following snippet clearly shows our original code may contain a bug (or a bad variable name – naming is hard!)



var million = 10000000; // no digit separators var million = 10_000_000; // digit separators added

ReSharper has a quick-fix which lets us add digit separators to any numeric literal:

Note that ReSharper adds digit separators at the expected location (thousands separator), but it’s perfectly possible to place them anywhere in a number:



var number = 17_15_1337;

byte b = 0b1010_1011;

Digit separators also are a language onramp for binary literals. It’s now possible to specify bit patterns directly instead of having to know hexadecimal notation by heart.ReSharper can easily convert a number to binary notation and back, too:

ReSharper 2016.3 already includes support for C# 7 and VB.NET 15 binary literals and digit separators.

Local functions

When coding, we often have to create a helper function. Sometimes, such helper function should only be available for one single method, so it would make sense to add it inside that method. That’s exactly what local functions are. With C# 7, we can declare helper functions inside other function bodies as a local function:



public static int Factorial(int number) { int Multiply (int num) { return (num == 1) ? 1 : num * Multiply(num - 1); } return Multiply(number); }

ReSharper already provides some support for local functions, and provides a basic set of code inspections for them, such as Possible System.NullReferenceException, Access to disposed/modified closure, and more. We also updated some of the existing refactorings and quick-fixes we have, such as this one where we can convert a statement body to an expression body, and back.

This already existed for C# 6.0, but now it works for C# 7.0 local functions, too. Note in this video we can also see that a recursive call was detected in our local function.

Local functions are comparable to lambda functions, but they are a bit more readable and quite useful if we want to write a function that is called only within the context of another method. In our ReSharper 2017.1 EAP, we added two new quick-fixes that allow converting a lambda expression or anonymous function to a local function:

Our codebase will mature, and the local function may have to become a proper method of its own. No problem! We have you covered on the refactoring front:



Support for local functions is still under development. And not only on our side – the language design itself isn’t fully carved in stone either – C# 7.0 is still evolving. For example, we had to wait for a PR from the C# compiler team before we knew whether supporting [NotNull] / [CanBeNull] annotations with local functions to help static analysis would be possible.

Output variables

In C#, using out parameters is not very fluid. For example, we always have to declare a variable with its full type before we can do a method call using that variable:



int number; if (int.TryParse("42", out number)) { // ... work with number ... }

if (int.TryParse("42", out int number)) { // ... work with number ... }

for

foreach

while

while(int.TryParse(Console.ReadLine(), out int x)) { // ... } Console.WriteLine(x); // <-- x not available here

C# 7 introduces output variables, making it possible to declare a variable right at the point where we’re making our method call:While ReSharper 2016.3 already recognizes output variables, there are some things we’re improving. An example would be detection of output variable scope. In the above code snippet, the variable number becomes available in the outer scope. This means we can use the number variable outside of our if statement as well. With a loop likeor, this does not happen though.We keep a very close eye on the .NET repo on GitHub to make sure we’ll have fantastic C# 7 support in ReSharper!

Other things we are working on are navigation to the out var ‘s type – navigation to out variable itself already works in ReSharper 2016.3.

C# and VB.NET Tuples

What options are there today to return more than one value from a method? We can use out parameters, but they are not very pleasant to work with and not supported when writing async methods. We can use System.Tuple<...> as a return type, but those allocate an object. On top of that, consuming code then has to put up with knowing what the Item1 , Item2 etc. properties mean. Specific types or anonymous types are two other options, but those are not very efficient to write/run either.

Both C# 7 and VB.NET 15 now adds tuple return types and tuple literals. For example:



public (string, string) FindPersonName(int id) { // ... lookup data ... return (firstName, lastName); }

Public Function FindPersonName(Id As Integer) As (String, String) '... lookup data ... Return (FirstName, LastName) End Function

var personNames = FindPersonName(123); var firstName = personNames.Item1; var lastName = personNames.Item2;

Item1

Item2

FindPersonName

public (string FirstName, string LastName) FindPersonName(int id) { // ... lookup data ... return (firstName, lastName); }

var personNames = FindPersonName(123); var firstName = personNames.FirstName; var lastName = personNames.LastName;

Deconstruction

Or in VB.NET:The calling code will get back a tuple with two string properties, which we can then work with:That’s… nice, but not very. We’re still usingandhere. Again, C# 7 and VB.NET 15 to the rescue! We can update ourmethod and name our tuple elements:The consuming code could now look like this:Much better! We’re building support for tuples in ReSharper, adding and updating inspections, quick-fixes and refactorings. One of those refactorings is Transform parameters”, where we will allow converting a method utilizing out parameters to a method using multiple return values via tuples. As with all new language features, some of them are still moving targets. For example if C# adds support for tuples in using directive , we will, too. To be continued!

In the above example with tuples, we are still using an intermediate tuple, personNames , which we’re then assigning to a firstName and lastName local variable. C# 7 introduces deconstruction – a clean way of consuming tuples. New syntax has been introduced for splitting a tuple into its parts and assigning those parts to new variables:



(string firstName, string lastName) = FindPersonName(123); // ... work with firstName and lastName ...

Deconstruct

KeyValuePair<TKey, TValue>

.Key

.Value

// Extension method public static class KVPExtensions { public static void Deconstruct<TKey, TValue>( this KeyValuePair<TKey, TValue> kvp, out TKey key, out TValue value) { key = kvp.Key; value = kvp.Value; } } // Consuming a dictionary foreach (var (key, value) in dictionary) { var item = $"{key} => {value}"; }

Deconstruct

Deconstruction not only works for tuples. It works for any type, as long as that type implements themethod, either directly or via an extension method. For example, we could write an extension method forwhich lets us use the key and value directly – avoiding having to useexpressions.We can add multiple overloads for themethod, taking a different number of out parameters. This lets us support deconstructing into various different variable sets.

ReSharper currently does not support deconstruction yet.

Pattern matching

This is one feature I personally like a lot. C# 7 adds new syntactic elements that allow us to test whether a variable has a certain “shape” or type and then start working with it. Here’s an example:



public void Dump(object data) { if (data is null) return; if (data is int i) Console.WriteLine($"Integer: {i}"); if (data is float f) Console.WriteLine($"Float: {f}"); }

data

null

data

In the above sample, we are doing several pattern matches. A constant pattern match checks whetheris. The next two checks are type pattern matches, testinghas a certain type and immediately extracting the value into a new variable with that type. Nice, no? Variables introduced by a pattern are similar to output variables described in that they can be declared in the middle of an expression and can then be used within the nearest surrounding scope.

Another nice example where we’re using patterns and output variables together to check, cast and parse all at once:



if (data is int i || (data is string s && int.TryParse(s, out i)) { // ... use i here ... }

is

switch

when

switch (vehicle) { case Car lada when (lada.Make == "Lada"): Console.WriteLine("<Lada car>"); break; case Car car: Console.WriteLine("<generic car>"); break; case Van van: Console.WriteLine("<generic van>"); break; default: Console.WriteLine("<unknown vehicle>"); break; case null: throw new ArgumentNullException(nameof(vehicle)); }

x is null

is

as

null

Others

ReSharper 2016.3 already partially supports pattern matching forexpressions andstatements, although we’re still working on supporting a really nice feature of pattern matching where we can write code that checks for type information as well as specific property values using thekeyword:ReSharper does not yet fully understand this syntax – we’re working on that and looking at making our existing code inspections and quick-fixes aware of new C# 7 syntax. We’ll have to recognizeas a null check, suggest using anexpression instead of ancheck, …

C# 7 and VB.NET 15 come with some additional language enhancements that we’re also adding support for in ReSharper but are still in early stages in our nightly builds (nightlies, one of the perks of working at JetBrains!)

We’re working on properly parsing throw expressions and wiring this into existing inspections, quick-fixes and refactorings.



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

ref

ByRef

public ref int Find(int number, int[] numbers); // return reference ref int index = ref Find(7, new[] { 1, 2, 3, 7, 9, 12 }); // use reference

Default Public ReadOnly Property Item(index As Integer) As ByRef Integer

ref

ByRef

In our code we could already pass variables by reference (using). C# 7.0 and VB.NET 15 now also let us return from a method by reference.Support forreturns is something we’re working on, too.

Many of our existing features have to be adapted or even rethinked to work with a new language version (and of course, should remain compatible with older language versions). We need to make sure existing ReSharper suggestions and inspections still make sense and produce correct and idiomatic C# code.

Our C# 7.0 and VB.NET 15 support is definitely not complete yet. We did want to provide an overview of what the programming language is evolving to and what we are currently working on in terms of supporting that. With ReSharper 2016.3, we provide early support for:

C# 7.0/VB.NET 15 binary literals and digit separators (parsing, several context actions, support for literals written in different base/separated differently)

Support for C# local functions (parsing, analyzing null)

Limited support for C# pattern-matching expressions (as the language design itself is not finished), C# output variables, C# tuples and C# deconstruction

All work in progress, but if you are using any of the new language features we’d love to hear your feedback! Download ReSharper Ultimate 2016.3, or even ReSharper Ultimate 2017.1 EAP, and give it a try!