One of my favorite things about C++ was its predictable stack unwinding. This could be combined with anonymous scopes for some powerful techniques. (I learned all about this from Steve) For example, I might want to hold onto a lock to something for a particular time. I could simply make an object which allocates the lock in its constructor and releases it in its destructor.
// Anonymous scope for lock
{
ResourceLock lock;
//…
}
ResourceLock allocates the resource it its constructor. It lives on the stack until the local block is exited, whereupon its destructor is called and the lock is released.
The really great thing about this technique, especially for things like locks, is that it’s entirely exception safe. Can we do something similar in C#?
Method 1: IDisposable/using
The C# way of doing predictable allocation is IDisposable. And since we have a bit of syntax sugar in the ‘using’ block to make it exception safe, we may as well use it for this purpose.
class ResourceLock: IDisposable
{
public ResourceLock()
{
// allocate the lock
}
public Dispose()
{
// release the lock
}
}
// usage example
using(new ResourceLock())
{
// the lock is held here…
}
// … and released here
Hopefully the usage is pretty obvious. I like this method because the usage syntax is pretty good. In fact I don’t know of another method for things like locks. But there are other scenarios where you might want to do a bit more.
Method 2: Delegate passing
You’ll be familiar with this technique if you’ve ever used Ruby or Smalltalk. Those languages (and others) have native support for blocks. It’s not quite as nice in C# but still usable.
Let’s take the example of data binding. There are often cases where you need to do a batch of operations on a class which can throw change events. While it’s not strictly incorrect to throw an event after each operation, it can be a big performance problem. So we want disable change notification while the operations are in progress and throw a single change event when they’re done. Think of it as a kind of data-binding transaction.
We’ll implement the Transaction method using a Delegate parameter:
// A class which is observable in some way
class DataBindingClass
{
public void Transaction(Action a)
{
try
{
SuspendChangeNotification();
a();
}
finally
{
ResumeChangeNotification();
NotifyObserversOfChange();
}
}
}
By passing an anonymous delegate to this method, we get an acceptable (but not great) syntax.
public void DoStuff(DataBindingClass db)
{
db.Transaction(delegate()
{
// do some stuff
});
}
Don’t forget that you can pull in variables from the surrounding scope via closure. That’s really the only reason this technique is worth bothering with.
Method 3: IEnumerable/yield return/continuations
Okay, we’ve now stepped clearly into the realm of abuse. Please don’t do this in your code. But in the interest of completeness…
Since the yield return syntax is really a form of continuation, we can get the same general scoping effect with a little bit of creativity.
// A class which is observable in some way
class DataBindingClass
{
public IEnumerable<int> Transaction()
{
try
{
SuspendChangeNotification();
yield return 42;
}
finally
{
ResumeChangeNotification();
NotifyObserversOfChange();
}
}
}
// usage example
public void DoStuff(DataBindingClass db)
{
foreach(var i in db.Transaction())
{
// do some stuff
}
}
As you can see, we abuse the ability of foreach to iterate over any IEnumerable to simulate a block syntax. In this case the block is only called once, so the loop only runs one time. The particular value we yield out Transaction() is completely arbitrary, as is the type. It’s only there to fool the loop into running.
In summary
C# gives us some reasonable tools here. I recommend method 2 (delegate passing) for most cases – it’s very flexible and the syntax is reasonable.
You must be logged in to post a comment.