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

Written by powerz | Published 2023/10/11
Tech Story Tags: c-sharp | dotnet | programming-tips | c | c-8.0 | object-initialization | using-declaration | c-programming

TLDRvia the TL;DR App

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:

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

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:

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.


Written by powerz | Create scalable, high-load, fault tolerant and distributed backend services and APIs.
Published by HackerNoon on 2023/10/11