01 DisposeAction

When using lambda functions, it is often convenient to pass around instances of Action<> or Func<>. In combination with the ‘using’ keyword, scoping can get a very flexible feature. Just a simple struct helps capturing Actions as an IDisposable:

public struct DisposeAction : IDisposable
{
  readonly Action _action;
 
  public DisposeAction(Action action)
  {
    _action = action;
  }
 
  public void Dispose()
  {
    _action();
  }
}

Now, imagine for example a stack where you want to push an element temporarily on:

var stack = new Stack<int>();
stack.Push(42);
  useTheStack(stack);
stack.Pop();

Seems fine, but it’s not complete yet. We want the stack cleaned when an exception is thrown in ‘useTheStack’:

var stack = new Stack<int>();
stack.Push(42);
try
{
  useTheStack(stack);
}
finally
{
  stack.Pop();
}

Admittedly, this is a frequently recurring pattern that can be spotted in C# code. But with the above DisposeAction, we can do better:

public static IDisposable scopedPush<E>(this Stack<E> stack, E element)
{
  stack.Push(element);
  return new DisposeAction(() => stack.Pop());
}

The above code can now be replaced by:

var stack = new Stack<int>();
using (stack.scopedPush(42))
  useTheStack(stack);

Fine, and we get the try/finally for free :)