The purist in me would want to do something like this:
void DoSomethingWithStream()
{
try
{
System.IO.FileStream fs = new System.IO.FileStream(@"C:\test.txt", System.IO.FileMode.Open);
try
{
// do something with the file stream
}
catch (Exception ex)
{
// handle exceptions caused by reading the stream,
// if these need to be handled separately from exceptions caused by opening the stream
}
finally
{
// FileStream.Close might throw an exception, so put FileStream.Dispose in a separate try/finally
fs.Dispose();
}
}
catch (Exception ex)
{
// handle exceptions that were either thrown by opening the filestream, thrown by closing the filestream, or not caught by the inner try/catch
}
}
Taken to the extreme, though, this would be messy:
void DoSomethingWithStream_PartDeux()
{
try
{
System.IO.FileStream fs = new System.IO.FileStream(@"C:\test.txt", System.IO.FileMode.Open);
try
{
try
{
// do something with the file stream
}
catch (Exception ex)
{
// handle exceptions caused by reading the stream,
// if these need to be handled separately from exceptions caused by opening the stream
}
finally
{
fs.Close();
}
}
finally
{
// FileStream.Close might throw an exception, so put FileStream.Dispose in a separate try/finally
fs.Dispose();
}
}
catch (Exception ex)
{
// handle exceptions
}
}
Accessing a database could be even worse:
void DoSomethingWithDatabase()
{
var connection = new System.Data.SqlClient.SqlConnection("Connect to mah database!");
try
{
var command = new System.Data.SqlClient.SqlCommand("Get mah data!", connection);
connection.Open();
try
{
var reader = command.ExecuteReader();
try
{
try
{
// read data from data reader (duh)
}
finally
{
reader.Close();
}
}
finally
{
reader.Dispose();
}
}
finally
{
connection.Close();
}
}
finally
{
connection.Dispose();
}
}
But then, in most cases I don't really see a need to explicitly close your streams / connections / data readers if you are going to dispose of them immediately afterward (unless you're just really paranoid). So, the database code above could just as easily be this:
void DoSomethingWithDatabase_PartDeux()
{
using (var connection = new System.Data.SqlClient.SqlConnection("Connect to mah database!"))
{
var command = new System.Data.SqlClient.SqlCommand("Get mah data!", connection);
connection.Open();
using(var reader = command.ExecuteReader())
{
// read data from data reader (duh)
}
}
}
Maybe I've just been tainted from coding with Dr. Wily's evil API. Using the initialize-variable-to-null trick doesn't work with his framework:
void DoSomethingWithDrWilyEvilBoobyTrap()
{
Dr.Wily.Evil.BoobyTrap trap = null;
try
{
trap = new Dr.Wily.Evil.BoobyTrap(Dr.Wily.Evil.Evilness.Very);
// do something with booby trap
}
catch (Exception ex)
{
// handle exceptions
}
finally
{
if (trap != null) // Exception thrown here!
trap.Dispose(); // Exception thrown here as well!
}
}
Here's a sneak peek at some of the souce code in his API:
public enum Evilness
{
Slight,
Moderate,
Very,
}
class BoobyTrap : IDisposable
{
public Evilness Evil { get; protected set; }
public BoobyTrap(Evilness evil)
{
this.Evil = evil;
}
public void DoEvil()
{
// ... snip (sorry, it's just too evil) ...
}
public static bool IsNull(BoobyTrap instance)
{
throw new Exception("I bet you thought this function would work, didn't you? Well it doesn't! You should know whether or not your variables are null. Quit asking me!");
}
public static bool operator !=(BoobyTrap x, object y)
{
if(y == null)
throw new Exception("You cannot check if an instance of a BoobyTrap is null using the != operator. Mwahahaha!!!");
return x.Equals(y);
}
public static bool operator ==(BoobyTrap x, object y)
{
if (y == null)
throw new Exception("You cannot check if an instance of a BoobyTrap is null using the == operator. Mwahahaha!!!");
return x.Equals(y);
}
#region IDisposable Members
public void Dispose()
{
switch (this.Evil)
{
case Evilness.Moderate:
case Evilness.Very:
throw new Exception("This object is cursed. You may not dispose of it.");
}
}
#endregion
}