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?
At the end of using statement block, it automatically calls the Dispose method.
The using statement causes the object itself to go out of scope as soon as Dispose is called. Within the using block, the object is read-only and can't be modified or reassigned. A variable declared with a using declaration is read-only.
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.
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... }
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With