Using the Using Block
Nov 9, 2018 • 8 Minute Read
The Problem This Solves
Setting up variables and tearing them down is a very common pattern – while we can perform this with a try-finally approach, we can organize It more elegantly with a _using _statement.
Code that follows this pattern:
var ourObject = new BusinessObject();
try
{
ourObject.DoStuff();
}
finally
{
ourObject.Dispose();
}
Can be rewritten more concisely like this:
using (var ourObject = new BusinessObject())
{
outObject.DoStuff();
}
Our Scenario
As we write code for our application, now that we know what it looks like, we're finding that the pattern of set up object, act with the object, and then dispose of the object is everywhere. We're implementing this with try-finally everywhere. One day in a code review, another developer says, "There's nothing wrong with the try-finally structure, but have you considered wrapping the variable in a _using _statement instead?"
After a little digging, you learn that the using structure takes a variable, creates a local scope to act with the variable, and then calls the Dispose() method on it when execution leaves the using statement. So we can refactor the try-finally segment of our code:
try
{
priceClient.Connect();
var updatedPrice = priceClient.GetPrice();
return updatedPrice;
}
/// catch blocks
finally
{
priceClient.ForceClose();
}
To look like this:
using (var priceClient = new PriceClient())
{
priceClient.Connect();
var updatedPrice = priceClient.GetPrice();
return updatedPrice;
}
Or can we? Placing the priceClient variable inside the using block means that Dispose() is going to be called on it – what exactly does that mean? I mean, forcing the closing of a socket seems like something that should be done when you're disposing of an object, I guess, but we need to be surer about what's going on here.
Implementing IDisposable
The docs say that stuff in a using block must implement IDisposable – what is IDisposable and how is it implemented? In formal terms, IDisposable is an interface in the System namespace – pretty core and essential stuff. IDisposable is literally the simplest possible interface, the simplest possible meaningful interface anyhow – it has a single, void method:
public void Dispose ();
This is where the Dispose method that the using block calls comes from – because any variable in a using block is guaranteed to have Dispose, the compiler knows that we can call it. A key point:
What precisely the Dispose() method disposes of is entirely up to the developer.
If you were imagining that there was any kind of framework or resource management magic going on, just because we implemented a particular interface and called it Dispose , you were wrong. The Dispose method is just an agreed-upon place to put your cleanup logic – it's up to the developer to make sure that that cleanup logic does what it's supposed to. This is all a long way of saying that just because you're calling dispose on your PriceClient, that doesn't mean that it's calling ForceClose or closing the connection properly in another way.
Luckily, because your vendor's library is open source, you can review the actual contents of Dispose on the object:
class PriceClient: IDisposable
{
public void Dispose()
{
this.ForceClose();
}
This means that this code:
using (var priceClient = new PriceClient())
{
priceClient.Connect();
var updatedPrice = priceClient.GetPrice();
return updatedPrice;
}
Does exactly the same thing as this:
try
{
priceClient.Connect();
var updatedPrice = priceClient.GetPrice();
return updatedPrice;
}
finally
{
priceClient.ForceClose();
}
But only because we have verified that Dispose() calls ForceClose. If we hadn't, or it didn't, we wouldn't know for sure that the two statements are equivalent.
Catching Exceptions in a Using Block
What about the catch blocks in our original structure?
catch (InvalidCastException)
{
return decimal.Parse(priceClient.ReturnedValue.Substring(1));
}
catch (TimeoutException timeout)
{
return null;
}
catch (ConnectionFailedException connectionFailed)
{
var serverName = connectionFailed.ServerName;
// log servername details here
return null;
}
Our using block has covered the try and finally portions of our error handling, but not the catch blocks. Using statements offer us nothing in that regard – to catch exceptions, we need to place a try-catch structure inside the using statement:
using (var priceClient = new PriceClient())
{
try
{
priceClient.Connect();
var updatedPrice = priceClient.GetPrice();
return updatedPrice;
}
catch (InvalidCastException)
{
return decimal.Parse(priceClient.ReturnedValue.Substring(1));
}
catch (TimeoutException timeout)
{
return null;
}
catch (ConnectionFailedException connectionFailed)
{
var serverName = connectionFailed.ServerName;
// log servername details here
return null;
}
}
This will create behavior truly equivalent to our original structure.
What if an exception occurs in the constructor for PriceClient? This exception would have occurred outside of our original error handling:
var priceClient = new PriceClient();
try
{
priceClient.Connect();
var updatedPrice = priceClient.GetPrice();
return updatedPrice;
}
Which is to say that if the exception occurs prior to the try block, it won't be caught. The same is true in our using statement – if an exception occurs initializing the using variable:
using (var priceClient = new PriceClient())
{
The object has not been instantiated, so you can't call Dispose on it. All this leads to a principle:
Be very careful about throwing exceptions in constructors.
You can't simply say "never throw exceptions in constructors" – you still need some way of signaling to the call stack that there's a problem. If you find yourself running into problems with this approach, consider using the Static Factory pattern to create your object.
What the Absence of a Catch Block Means
In performing technical interviews, one of the questions I would ask to establish how well the applicant understood structure error handling is this: "In reviewing some code, you find a try-finally block with no catch blocks inside. What is the intention of the developer who wrote this?"
I often heard, "you should always have a catch block," and sometimes, "I don't think that will even compile". A try-finally with no catches is entirely valid – what this signifies is that the current scope of execution can do nothing to resolve the exception and will simply pass it up the chain. If you have implemented the Top-Level Error Handling Pattern we talked about in the second objective, it will be handled and perhaps logged there so a developer can figure out what to do with it.
One good example of this is when a critical resource for an application is unavailable, like the database. If I've got a web page that is calling out to the database, there's not anything I can do to recover from this situation – I need to just show an error page and be done with it, which I can make the top-level error handler do. If I try and handle that exception in a low-level routine, at best, I'm going to be returning null as we looked at in the first objective, and at worst, I'm going to be hiding a problem, like we addressed in the second objective by making our catch statements more precise.
Only handle an exception if there's something you can do to fix it.