Peter A. wrote:
What is the most expedient way to do this C# construct in Ruby?
C#: using (myObj = new MyClass()) {
// misc code here
} //MyClass.Dispose automatically called here
I wish to put stuff in MyClass.Dispose() to release resources… it must
always be called.
In a language with higher-order procedures like Ruby, adding a special
language construct for this is completely unnecessary. You just write
a method which accepts a piece of code as an argument, acquires the
resource, runs the piece of code and subsequently releases the
resource. This is the well-known “with-foo” pattern from Lisp,
basically. In Ruby, we generally use a factory-ish class method like
so:
class DatabaseConnection
def self.use(*args)
yield handle = new(*args)
ensure
handle.close
end
end
This is how usage would look like:
DatabaseConnection.use('/path/to/sqlite.db') do |conn|
conn.exec 'SELECT blah FROM blurb WHERE foo = 42;'
conn.exec 'INSERT something INTO somewhere;'
end
This pattern is for example used by IO.open in the standard library:
File.open('/path/to/file', 'w+') do |f|
f.puts 'Hello, World'
end
The two main differences between the two methods are:
- in the Ruby version, the acquisition of the resource is hidden away
inside the method, whereas in C#, the user is responsible for
creating the resource. (You could, of course, write a generic
method that simply takes any resource as a parameter. That’s just
not the normal style in Ruby.)
- in the C# version, there is a standardized agreement over what the
name of the method for releasing the resource is (the Dispose()
pattern) and there is even a type for it (the IDisposable
interface)
Here’s what a more C# style implementation in Ruby would look like:
def using(handle)
yield handle
ensure
handle.dispose
end
using(MyClass.new) do |my_obj|
# misc code here
end # MyClass#dispose automatically called here
BTW: you can do that in C#, too, now. It’s just that before lambda
expressions, local variable type inference and anonymous types were
introduced in C#, it would have been a lot of typing (in both senses
of the word). This is what the opposite would look like (i.e. Ruby
style implemented in C#):
class DatabaseConnection
{
static T use<T>(string connection, Func<DatabaseConnection, T>
block)
{
var handle = new DatabaseConnection(connection);
try
{
return block(handle);
}
finally
{
handle.Dispose();
}
}
}
And this is how you use it:
DatabaseConnection.use("/path/to/db", (conn) =>
{
// misc code here
}); // MyClass.Dispose automatically called here
(If you don’t want to return a value, lose the T and replace Func with
Action.)
jwm