Automatically disposing IDisposable?

Reading up on Ruby, I was intrigued by the way file operations are handled. Primarily the fact that Ruby allows you to pass a block as a parameter to Open method of a File object. When you do this it opens the file for you, passes the file to the block, and then closes the file when the block is done executing, relieving you of having to manage checking to make sure the file is open, as well as making sure that the file is closed when your are done using it.

Below is the example that is shown in Programming Ruby by Dave Thomas.

File.open("archive.log", "a") do |f|
  f.each_line { |line| puts "Got #{line.dump}" }
end

This ability seemed similar to how the FileStream returned from File.Open in C# implements IDisposable, and by putting that FileStream object in a using block, the .NET Framework will call disposable when that block is exited. But what struck me, was that every time you get a FileStream object, it needs to be put in a using block. I have stubbed out an example of this against a call to the method ExecuteReader on the SQLCommand class.

public class Test
{
	public void RunQuery()
	{
		string queryString = "testQueryString";
		SqlConnection connection = new SqlConnection("myConnectionString");
		SqlCommand command = new SqlCommand(queryString, connection);
		DbDataReader reader = command.ExecuteReader();
		using (SqlDataReader reader = command.ExecuteReader())
		{
			while (reader.Read())
			{
				Console.WriteLine(String.Format("{0}", reader[0]));
			}
		}
	}
}

While I do strongly agree the using block greatly improved the way we would have had to deal with closing the reader previously in .NET, after seeing the way Ruby deals with this. The reason I think Ruby handles this better is that for a number of scenarios the responsibility of disposing the object should be hidden from the consumer if possible. So I suggest that in the future, when designing your method calls the goal should be to make the disposing of the IDisposable automatic, at least from the point of view from the consumer. This helps reduce boiler plate code that has to be duplicated every where the method is used. The other reason that this is helpful, is that it prevents the consumer from forgetting to dispose of the object when they are done with it. It may also be helpful to create extension methods for those API calls you have no control over to give you this functionality as well, as shown below.

public static class FileExtensions
{
	public static void ExecuteReader(this SqlCommand command, Action block)
	{
		using (SqlDataReader reader = command.ExecuteReader())
		{
			block(reader);
		}
	}
}

public class Test
{
	public void RunQuery()
	{
		string queryString = "testQueryString";
		SqlConnection connection = new SqlConnection("myConnectionString");
		SqlCommand command = new SqlCommand(queryString, connection);
		command.ExecuteReader(reader =>
		{
			while (reader.Read())
			{
				Console.WriteLine(String.Format("{0}", reader[0]));
			}
		});
	}
}

Based on this we can even enhance this functionality and encapsulate the DbDataReader completely and pass a different type of object to the block if we decide that we have a better abstraction to work against.

Note: I almost decided not to post this, as I was at the Dallas Day of Dot Net this weekend and pinged Derick Bailey about this for his perspective as a developer who used both Ruby and .Net, and he mentioned his blog post How Ruby Taught Me To DRY Up My Code With Lambda Blocks on the topic.  So go read his entry as well, and try this out for yourself and let me know how this works for you.

2 thoughts on “Automatically disposing IDisposable?

  1. Saad R

    Proctor,

    A responsibility / service boundary exists between a caller and callee, part of which is the pre and post state of the arguments passed into it as well as the return type. Most times, the caller should make the decision about disposing as it has higher scope.

    Here’s some points to clarify the distinction I’m trying to make:

    – The method you’ve written should really be named ExecuteAndCloseReader
    – SqlCommand also implements IDisposable, yet its not being handled in this code and probably should be
    – SqlConnect also implemented IDisposable and should most likely not be Dispose()’ed in this code block as its more reusable / should be managed at a higher scope
    – An alternate:
    – The Action contains only Console.WriteLine(String.Format(“{0}”, reader[0]));
    – Our method takes connection, queryString, block and manages life cycle for the command and reader
    – Our method is renamed to ExecuteInReaderContext. It provides the context, then removes it

    1. Proctor

      Saad,

      I agree with you comments, with the possible exception to ExecuteAndCloseReader, but given the time since I wrote this, I am not sure that I even like my original name. I do think the ExecuteInContext leans to a better name for the method.

      When I originally posted this, it was to get the idea out of my head, and into the “world” for discussion. I have since applied this idea, and we did in fact have the method which pretty much matched the one you outlined.

Comments are closed.