Monday 18 June 2007

Readable RAII in C# ?

[This blog has moved and this article can now be found at http://www.levelofindirection.com/journal/2009/9/24/raii-and-readability-in-c.html. It still appears here for archival purposes]

In my last post I made the following comment:

"Of course, in C# there is the using keyword, along with the IDisposable interface, which gives you a little more C++-like scoped disposal. Even this is less clean and more awkward than the C++ model, and I believe also has scalability problems (caveat - I haven't really used this in real C# programs)."

Since that time I have been using C# almost exclusively and have had a chance to explore the using statement a bit more. In this post I'm going to expand a little on the theme of: "I believe [the using statement] also has scalability problems". How true is that statement, and what can we do to improve on things?

First - a quick review: what is the using statement?
Well, C#, like Java, but unlike C++, has non-deterministic destructors. That is, you don't know when destructors will be called because they are called by the garbage collector, rather than the point at which the object they are to be called on goes out of scope.

In C++, having deterministic destructors is very useful for implementing RAII (Resource Acquisition Is Initialization) techniques, a big part of which is cleaning up resources in the destructors. This includes, but is not limited to, memory allocations. Other resources could be file handles, database connections, GUI drawing object handles, or even more abstract "resources" such as closing tags in an XML outputer!

The subtlety comes in the face of exceptions - which could occur at moments that are difficult to determine from the static code. I'm glossing here, of course, because many other articles have been written on the details of RAII and exception safety.

In Java you can only really deal with exceptions when you have resources other than memory by using the try-finally construct. The finally clause gives you a place to put common clean-up code. You can abstract this further using Execute-Around. All this is covered in my previous post.

In C# the story is much the same, with try-finally available. But C# goes one better by supplying the using statement. The using statement gives us back much of what we had in C++, by allowing a method on the object (Dispose, a method of the IDisposable interface that you must derive from) to be called at the end of the scope - whether that scope is exited by the normal flow of execution, or by an exception.

However, we still have to put something in the client code - the using statement itself - and if you follow the traditional patterns you do, indeed, reach a scalability problem in terms of readability.

To illustrate, imagine you have a class, Foo, which implements IDisposable. To use it you'd write code much like this:
  using( Foo foo = new Foo() )
{
// do stuff with foo here

} // <-- foo's Dispose method is called here

This looks nice and clean so far. But imagine if you need three Foos:
  using (Foo foo1 = new Foo())
{
using (Foo foo2 = new Foo())
{
using (Foo foo3 = new Foo())
{
// Do stuff with foos here
}
}
}

Contrast that with what we'd need to to similar in C++:
  { 
Foo foo1;
Foo foo2;
Foo foo3;

// Do stuff with foos here
}

In our C# version, the more disposable objects you have the more the readability suffers!
Imagine too, that the declarations are more complex (as they usually will be), and things start to get messy.

But how often do you really need so many objects with deterministic disposal in C#? After all most things are going to be managed anyway, aren't they?

Well, one common example might be with database connections, along with transactions, command objects, etc - all needing to be composed together, then disposed of at the right times.
Another might be something that generates XML or HTML. Even when using such things as XmlWriter there are times it is helpful to represent the hierarchy with RAII.

So, can we improve on this situation?

Well, the first thing to note is that there is a way to assign multiple objects to one using statement in C#. However there is one important caveat: all the objects must be of the same type!
  using (Foo foo1 = new Foo(),
foo2 = new Foo(),
foo3 = new Foo())
{
// Do stuff with foos here
}
This is certainly better. However, if the declarations get more complex it can tend to suffer from readability problems along similar lines to passing anonymous delegates to methods - too much code between commas!
Also, that single type limitation is a cruel one. It won't help with our database connection, transaction etc problem.

However there is another way! It allows different types to be declared. It can co-exist with the previous construct for multiple declarations of the same type, and it doesn't use any new language features! (thanks to George Moudry for suggesting this).
In fact, on first appearances it might even appear a bit of a hack - but bear with me here.
The secret is to go back to our first example - with the nested using statements. The trouble was all the redundant braces and indentation. So remove them! What? Yes, you don't actually need them! the braces just allow you to write multiple lines of code and have it appear to the compiler as a single block. However, if your multiple lines are another using statement, then it's already a single block.
Before you bring out the flame-throwers, take a look at this example:
  using (Foo foo1 = new Foo())
using (Foo foo2 = new Foo())
using (Bar bar = new Bar())
{
// do stuff with foos and bars here
}

It actually looks quite neat. How about something more realistic (thanks to Vishal Doshi for this example):
  using (new System.Transactions.TransactionScope())
using (OracleConnection conn = m_DB.GetConnection())
using (OracleTransaction tx = conn.BeginTransaction())
{
//use conn, tx within the transaction scope
}

Also, remember I said you can mix it with C#'s construct for multiple declarations of the same type. Our foo-bar example could be written as:
  using (Foo foo4 = new Foo(),
foo5 = new Foo() )
using (Bar bar = new Bar() )
{
// Do stuff with foos and bars
}
So, in conclusion: I still prefer the elegance of C++'s RAII capabilities - but creative use of C#'s using statement make using using more usable!

Friday 16 February 2007

RAII and closures in Java

[This blog has moved and this article can now be found at http://www.levelofindirection.com/journal/2009/9/24/raii-and-closures-in-java.html. It still appears here for archival purposes]

One of the biggest reasons I still prefer C++ over newer brethren such as Java and C# is its support for RAII (Resource Acquisition Is Initialisation). Unlike C++, Java and C# have the finally construct, to give you a place to do clean up of resources. The trouble is this doesn't scale very well. If you have several resources in a scope you may have to nest try-catch-finally blocks. A good article describing this problem from a Java perspective is here.

Of course, in C# there is the using keyword, along with the IDisposable interface, which gives you a little more C++-like scoped disposal. Even this is less clean and more awkward than the C++ model, and I believe also has scalability problems (caveat - I haven't really used this in real C# programs).

One way to work around the lack of deterministic destruction in such languages is to use the Execute-Around idiom (more detailed coverage in this pdf). This allows you to factor out the resource acquisition, initialisation and release code, from the code that uses the resource. It scales better than inline try-catch-finally, but can result in much more fragmented (using a named class) or messy (using an anonymous class) code. In some cases the fragmentation may be a good thing - after all Extract Method is a valuable refactoring tool. But other times it may be too much, especially in such languages with a more imperative bias.

In languages with decent support for Closures, Execute-Around can be a much more natural and pleasant experience with less fragmentation (although more because of the side-effect of decent language syntax, than because of the formal benefits of closures).

But while C# has closures, as of .Net 2.0, Java doesn't. That is, not at the time of this writing. There are serious proposals afoot to add them to the language - and from the sound of this blog entry from Neal Gafter (former Java co-designer, now at Google),they should have all the features that make them useful for techniques such as Execute-Around (as well as a whole load of other benefits, of course, but you can read about this elsewhere - such as in the cited article).

Perhaps the time will yet come when I can use Java seriously without too many grimaces!

Thursday 15 February 2007

ACCU conference

How better to kick off my technoblog than to mention the upcoming ACCU conference, and, of course, my involvement in it :-)

For anyone wondering what the ACCU is, it stands for the Association of C and C++ Users, but its membership covers a broader range of, mostly C-family, languages (inc. Java, C# and a number of dynamic languages). The group has a number of industry names in its fold - and a friendly, pub-like, atmosphere.
The conference is, according to many, the best programming related conference around, attracting top names such as Stroustrup, Sutter, Alexandrescu, Meyers and many others. At the same time many of the unlettered membership are given the opportunity to stand on the shoulders of giants, and this year I'll be one of them!

My presentation is on the subject of "Meta-Intelligent Programming", and is a continuation of my "Organic Programming" concept that I kicked off at the ACCU conference three years ago. I'll start writing more about Organic programming itself in my dedicated blog (http://organic-programming.blogspot.com/) - but suffice to say that it is not about programming, per se, but rather about how we use our minds (and bodies) more effectively in a software development environment. Think "NLP for software development" and you won't be far out. It mixes in a lot of Tony Buzan stuff too.

What's this blog about?

I'll be writing from my thoughts about technology in general or in specifics.
At least, that's the plan!