IDisposable in Ruby
about 1 year ago
I have been learning Ruby lately and have been very impressed with a language feature called code blocks.
Essentially it allows any method to return control and optionally data to the caller. It is used to implement iterators, transactions, profiling blocks etc…
So, I decided to see if it could be applied to the problem IDisposable:http://msdn2.microsoft.com/en-us/library/system.idisposable.aspx was created to solve.
Take the following C# code:
class Tester : IDisposable
{
public Tester()
{
Console.WriteLine("Allocating");
}
public void DoStuff()
{
Console.WriteLine("doing stuff");
}
public void Dispose()
{
Console.WriteLine("Deallocating");
}
class Program
{
static void Main(string[] args)
{
try
{
using (Tester t = new Tester())
{
t.DoStuff()
throw new Exception("something happened");
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
This simple example has a pile of issues.
- The designer of the “Tester” class has no way of ensuring that “Tester” objects are wrapped in using blocks (or at least always finally disposed)
- This can be partially worked around by defining a finalizer, tracking weather the object is disposed or not and suppressing the finalizer if the object is disposed properly. It is a real headache.
- The designer of the “Tester” object has no way of telling if the object was used successfully and no exceptions were thrown.
- .Net has no mechanism for asking the framework if exception handling blocks are currently being executed.
I rewrote it in Ruby:
class Tester def do_stuff() puts "doing stuff" end def initialize() raise "Must be called from a code block!" if !block_given? begin puts "Allocating resources" yield self rescue puts "An exception happend: " + $! raise ensure puts "Deallocating resources" end end end end begin Tester.new do |test| test.do_stuff raise "Something happened!" end rescue puts "Caught: " + $! end end
Ruby’s advantages:
- You can trap and track exceptions during the cleanup phase.
- You can ensure proper usage. (Raises an exception if not used from a code block)
- You need to write less code.
- It is a lot less mysterious. For someone new to .Net the relationship between the “using” keyword and the IDisposable interface may seem arbitrary.