paint-brush
Beware of 'Using Declarion' in C# 8.0: A Real Exampleby@powerz
125 reads

Beware of 'Using Declarion' in C# 8.0: A Real Example

by Aleksei ZagoskinOctober 11th, 2023
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Explore the unexpected behavior of the 'Using Declaration' feature in C# 8.0 and learn how to safely implement it in your code.
featured image - Beware of 'Using Declarion' in C# 8.0: A Real Example
Aleksei Zagoskin HackerNoon profile picture


Today, let's dive into the somewhat unpredictable and potentially risky behavior of the C# 8.0 "Using Declaration" feature. As always, no boring long introduction paragraphs; let's get straight to the point!

What is Using Declaration?

In C# 7.0 we did this:

public void SuperMethod()
{
    using (var myVariable = new SomeDisposable())
    {
        // use myVariable
    } // myVariable will be disposed here
}

Since C# 8.0, we can write this as follows:

public void SuperMethod()
{
    using var myVariable = new SomeDisposable();
    // use myVariable
} // myVariable will be disposed here

The lifetime of myVariable will extend to the end of the scope in which it is declared (in this case — SuperMethod containing method).

Looks marvelous! But only until we combine it with object initializers...

The Problem

First, we're going to take a look at this tiny piece of code:

using var response = new HttpResponseMessage
{
    Content = new StringContent("Hello, World!")
};
// some other code

At first glance, it looks perfectly fine: we create an instance of a disposable type and initialize one of its properties. When the program execution reaches the end of the method that contains this code, our response object will be disposed of (the same goes for when "some other code" throws an exception). In other words, we could expect this behavior:

var response = new HttpResponseMessage();
try
{
    response.Content = new StringContent("Hello, World!");
    // some other code
}
finally
{
    response.Dispose();
}

While intuition and common sense might lead us to expect this, the reality is quite different.

To understand what happens, let's check the "Low-Level C#" and IL for the first code snippet that we started with:

Low-Level C# for Using Declaration

IL Code for Using Declaration

Now we can see what is wrong with this lovely Using Declaration construction: if we use an object initializer on our instance, the values will be assigned to the properties before the try-catch block. Thus, if one of these setters throws an exception, we will end up with an allocated instance of a disposable type that is not going to be disposed of by the using (in the try-catch block).

Frankly, this implementation and design choice caught me off guard.

Solution

As you might have guessed, the workaround is straightforward: first, instantiate a disposable object using "Using Declaration," and then initialize its properties — avoid using object initializers.

Example

using var response = new HttpResponseMessage();
response.Content = new StringContent("Hello, World!");
// some other code

Low-Level C# for Using Declaration (good practice)

IL Code for Using Declaration (good practice)

Bonus

If you use an advanced IDE (JetBrains Rider) or an amazing plugin for Visual Studio (Resharper), it will warn you about this code issue:

JetBrains Rider vs wrong C# Using Declaration

initialize object properties inside the 'using' statement to ensure that the object is disposed if an exception is thrown during initialization


That's all for now. Thank you for reading; I hope it helps!


Cheers!


Also published here.