.NET Inside Out Part 21 – Using is broken

This is the twentieth first part of the .NET Inside Out series. For your convenience you can find other parts in the table of contents in Part 1 – Virtual and non-virtual calls in C#

Today we are going to see using in details across various .NET implementations (.NET Core, .NET Framework and Mono). I explicitly mention platform differences later on. The following code:

is conceptually translated to:

Seems simple enough but it has a lot of gotchas.

Losing exceptions

Let’s start with the following code:

We throw an exception in the try block and while cleaning up the resource (think of it as of an exception while closing a file, for instance). Output:

So we can see that the resource was not cleaned up at all! Why? That’s because there is no exception handler so in some implementations the finally block is not executed. Let’s try adding it:

Output:

Okay, we see that the Dispose method was called and threw the exception but we lost the exception from the using block.

Let’s see if it works in Java:

Output:

Okay, try with resources in Java works correctly, prints both exceptions.

So we see C# loses exceptions. Well, let’s continue.

Faulty constructor

What happens when constructor throws?

So we don’t enter the using block at all. At least, this is consistent with Java:

So we see that even though it looks like the resource is inside using, it is initialized before entering the try block. Something similar to old implementation of lock keyword.

Let’s fix it

There are two places we may get exceptions. One is in the using block, the other is in the cleanup code. We need to handle both of them, ideally without changing the types. Let’s take this code:

In liens 8 and 9 we prepare variables for storing exceptions. Next, we enter try block and do the same things we did previously.
In line 15 we capture the exception thrown, to be used later.
In lines 17-21 we try cleaning up the resource if there is a need.
Finally, in lines 23-28 we decide what to do. If we had two exceptions, we need to throw both of them. So we just use AggregateException. However, if we had only one of them (line 25 or 27), we rethrow it with preserving the context.

Output with two exceptions:

Exception during cleanup only:

Exception during using only:

Looks much better now.

Let’s fix it even more (Mono and .NET Framework)

.NET Core doesn’t allow us, but what if we abort the thread?

We create two threads and synchronize them using events. The outer thread tries to kill the inner one, which is in the middle of using. Output:

Looks pretty good. We swallow the exception and print it out, we have (almost) all the details. However, we also have a line This shouldn't appear. Remember that we cannot stop the thread from aborting just like that, we need to call ResetAbort. However, in this example we change this behavior. BTW, in this weird tio Mono version, by throwing in finally you can stop the thread from aborting, as shown here.

Okay, how to fix that? We need to abort the thread again but in some weird version of Mono in tio we cannot do it from its body (we can in .NET Framework). We need to grab another one, as shown here:

Output:

We miss one last thing, the original value passed to Thread.Abort("With old data"). But it can be extracted when logging, we just need to cast the exceptions correctly.

This seems to be working a bit better. Just wrap it into a helper method and that’s all.

Bonus chatter: here you can read about doing that in Scala.