How-To's

Declaration expressions in initializers and queries – C# 7.3 in Rider and ReSharper

Ever since C# 7.0 came about, the pace at which our beloved programming language evolves became more rapid. A few weeks back, C# 7.3 was officially released, adding several new features that can help us write cleaner, clearer and more concise code.

Let’s look at some of the new C# 7.3 language features and how the Early Access Preview (EAP) versions of ReSharper 2018.2 and Rider 2018.2 support it. In this post, we will cover declaration expressions in initializers and queries.

This post is part of a series:

Declaration expressions in initializers and queries

C# 7.0 already permitted the use of expressions containing expression variables. An example would be using an out variable declaration (more details in our previous series):

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

This pattern was not available for every expression. With C# 7.3, declaration expressions can now be used in field initializers, property initializers, constructor initializers and query clauses (spec). Here’s an example where we initialize a field with an out var declaration expression:

public class DeclarationExpressionsExample
{
    public bool inputIsPositiveNumber = int.TryParse(ReadString(), out var input) && input > 0;

    public static string ReadString() => Console.ReadLine();
}

It’s worth mentioning that the scope of these variables (in the above example, input) is limited to the containing initializer. So if we have multiple of these expressions in one type, they will not conflict:

public class DeclarationExpressionsExample
{
    public bool inputIsPositiveNumber = int.TryParse(ReadString(), out var input) && input > 0;

    public bool ObjectIsPositiveNumber { get; } = ReadObject() is string str
                                                && int.TryParse(str, out var input)
                                                && input > 0;

    public static string ReadString() => Console.ReadLine();
    public static object ReadObject() => Console.ReadLine();
}

(Note we snuck in another example of declaration expressions here, this time in a property initializer.)

ReSharper and Rider recognize this new C# 7.3 syntax, and are aware of their scope, which can depend on the construct where the declaration expression is used. For example when using a declaration expression in a constructor, ReShaper and Rider are aware that the declared variable is available within the full scope of that constructor:

public class DeclarationExpressionsExample
{
    private readonly bool WrapsPositiveNumber;
    public readonly int Number;

    private DeclarationExpressionsExample(bool wrapsPositiveNumber)
    {
        WrapsPositiveNumber = wrapsPositiveNumber;
    }

    public DeclarationExpressionsExample(string str)
        : this(int.TryParse(str, out var input) && input > 0)
    {
        // "input" is available in the scope of the constructor
        Number = input;
    }
}

Similarly in queries, the declared variable would be available within the scope of its containing clause, but not outside of it. So in the following example, we can make use of the number variable within our where, but not in our select.

// Works:
return from str in enumerable
       where int.TryParse(str, out var number) && number > 0
       select str;

// Doesn't work:
return from str in enumerable
       where int.TryParse(str, out var number) && number > 0
       select new 
       {
           A = str, 
           B = number // "number" is not available in this scope
       };

ReSharper and Rider will provide similar code analysis and refactoring functionality as with declaration expressions in other expressions, as declaration expressions in initializers and queries are mostly extending already existing language features.

So for example, when doing a type check and cast combination, ReSharper and Rider will suggest to use pattern matching instead:

C# 7.3 declaration expression support in ReSharper and Rider

Using declaration expressions in initializers and queries will definitely provide cleaner code in these additional cases.

Download ReSharper 2018.2 EAP now! Or give Rider 2018.2 EAP a try. We’d love to hear your feedback!

image description