IDisposable in Ruby

over 18 years 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 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

Join the discussion

What do you think?

comments powered by Discourse