Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When does IDE0063 dispose?

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?

like image 443
Bizhan Avatar asked Aug 12 '19 11:08

Bizhan


People also ask

Does using statement Call dispose?

At the end of using statement block, it automatically calls the Dispose method.

What is using () in C#?

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.


Video Answer


2 Answers

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... } 
like image 110
Henk Holterman Avatar answered Oct 02 '22 10:10

Henk Holterman


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.

like image 43
Neil Laslett Avatar answered Oct 02 '22 11:10

Neil Laslett