85

I'm trying to understand this C# 8 simplification feature:

IDE0063 'using' statement can be simplified

For example, I have:

void Method()
{
    using (var client = new Client())
    {
        // pre code...
        client.Do();
        // post code...
    } --> client.Dispose() was called here.
    // more code...
}

IDE tells me I can simplify this using statement by writing this instead:

void Method()
{
    using (var client = new Client());
    // pre code...
    client.Do();
    // post code...
    // more code...
}

I can't understand how it works and how it decides I'm not using the variable anymore. More specifically, when exactly does it call client.Dispose method?

H H
  • 263,252
  • 30
  • 330
  • 514
Bizhan
  • 16,157
  • 9
  • 63
  • 101

2 Answers2

70

You are using C# 8. In older C# versions that ; would have made this invalid.

In the new syntax, the client stays in scope for the surrounding method (or other {} scope block). Note that you can omit the outer pair of () as well.

It's called a using declaration, the documentation is here.

void Method()
{
    using var client = new Client();
    // pre code...
    client.Do();
    // post code...
    // more code...
} --> client.Dispose() is called here (at the latest)

Logically the Dispose happens at the } but the optimizer might do it earlier.

Edit

I noticed that having // more code after the end of the using block, prevents this improvement from appearing. So there will be no more ambiguity if you convert the following code:

void Method()
{
    // not relevant code

    using (var client = new Client())
    {
        // pre code...
        client.Do();
        // post code...
    }
}

into this code:

void Method()
{
    // not relevant code

    using var client = new Client();
    // pre code...
    client.Do();
    // post code...
}
H H
  • 263,252
  • 30
  • 330
  • 514
  • 1
    So OP put all effort into getting it, then completely ignored what was written and come to SO with new question and even made a mistake (his second snipped still using statement)? – Sinatr Aug 12 '19 at 11:20
  • 2
    So the IDE is wrong in suggesting a simplification which will actually defer the disposal to the end of “more code...”? – Holger Aug 12 '19 at 11:20
  • 5
    @Holger - you have a point but it will rarely matter. Follow standard practices for short methods etc. The motivation for this was reducing clutter (consider using 3 or 4 usings in one scope). – H H Aug 12 '19 at 11:23
  • 3
    I’m not objecting the feature in general. But I understand the OP’s confusion when an IDE makes a suggestion as if the two code snippets were equivalent. – Holger Aug 12 '19 at 11:28
  • @Holger I have tested this out. I was confused about several nested using blocks I had and it didn't call out. It will not recommend simplification if it does actually change when the .Dispose is called. Pretty clever code checking in here. It will only make the recommendation when it IS precisely equivalent. https://github.com/dotnet/roslyn/blob/8911c9fe424f153b308414356c61d7a87bca111c/src/Features/CSharp/Portable/UseSimpleUsingStatement/UseSimpleUsingStatementDiagnosticAnalyzer.cs#L173 – BillRob Aug 20 '20 at 05:52
  • 1
    Hmmm, I don't like this... simplification that could cause breaking changes later on. Maybe this is one of those things that should become a coding standard to avoid? Similar to deliberate braces for an if statement on-liner (?) – Riegardt Steyn Aug 30 '20 at 11:04
42

The short answer is that the new (optional) using statement syntax inherits its parent's scope.

I have to agree with the OP that this is a very confusing change in C# 8.0, for many reasons.

Historically, using has always operated with a scope like other blocks (if, switch, etc.). And like if, the using statement's scope was the next line or block of code.

So it is perfectly valid to write something like:

using (var client = new Client())
    client.Do();

This means client is only in scope for the single statement, which is great for single-line operations, like triggering a SQL stored procedure with no return value.

But now we also have:

using var client = new Client();
client.Do();

Which is not the same thing at all; client remains in-scope for the entire method.

Now, Visual Studio will only suggest this change if nothing came after your original using block, so it's functionally identical. But what if more code is added later? With the old scope notation, it was very clear whether the new code was in or out of scope. With the new syntax, everything after using is in scope, but that might not be clear.

The Roslyn team may have figured that this doesn't really matter. Unlike flow control statements (if, etc.), do you really care if your object stays in scope for a few more lines of code? Probably not. But like all things, it depends.

In some ways, it's an improvement since it clearly says, "Instantiate this object and call Dispose() when it goes out of scope." Objects are always destroyed and garbage collected when they go out of scope (i.e. method ends) but that does not mean that Dispose() is called. Adding using to a local variable declaration is just a way of making that happen.

Finally, and this is big, if you are targeting .NET Framework, you're probably not really using C# 8.0.

You may think you are; I did. You may be running Visual Studio 2019 16.3+. You may even have the latest version of the Microsoft.Net.Compilers package installed, and that says you're getting C# 8.0, right? But you're not. By default, .NET Framework is capped at C# 7.3.

In my tests, when I target .NET 4.8, Visual Studio is smart and won't offer C# 8.0 suggestions. But if I target an older version (4.7.2) I do get this suggestion, which then generates a build error. The IDE won't show you that error - your project looks clean - but you'll get two syntax errors when you actually build.

When targeting .NET 4.8, if you do try and use C# 8.0 syntax you'll get the friendly

CS8370 C# Feature is not available in C# 7.3. Please use language version 8.0 or greater.

and an offer to add <LangVersion>8.0</LangVersion> to your project file (even though that's officially unsupported by Microsoft). It works, with caveats. But with older .NET versions that doesn't seem to be the case. So show extreme caution when accepting these new syntax hints on older projects!

UPDATE: I was wrong about older NET Framework versions triggering the hint. The culprit was an old version (2.10.0) of Microsoft.Net.Compilers. That was the last version compatible with older versions of Visual Studio. After removing that package, the hint is no longer offered.

Neil Laslett
  • 2,019
  • 22
  • 22
  • 1
    yes, I ahd this too: CS8370 C# Feature is not available in C# 7.3. Please use language version 8.0 or greater... I had to undo the sugestions accepted...because of my C#7.3! – Teo Dec 03 '19 at 13:19
  • Thanks for the hint about `Microsoft.Net.Compilers`. That fact that VS kept suggesting refactorings that would lead to compile errors drove me crazy. – Heinzi Aug 19 '20 at 15:41
  • 1
    This worries me. Reminds me of Jon Skeet's story about if statements without braces - https://stackoverflow.com/a/8020255/1904753 – Riegardt Steyn Aug 30 '20 at 11:14
  • Agreed, that would be weird to have a `using` block without braces. I'm not saying I usually do that, just that these two syntaxes (one as block declaration, one as variable declaration modifier) are so similar that I find it confusing. – Neil Laslett Sep 01 '20 at 19:55
  • This should be the accepted answer. It tells me what I wanted to know in the first line. Henk's answer is good too, however it doesn't address the question directly. – Sasino Jul 16 '22 at 22:00