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.

Comments