Tuesday, January 10, 2012

Closures With Lambda Expressions in C#

Over the last few months, I have had the pleasure of spending most of my day (and night) learning and writing a pure JavaScript application. Coming from a strictly statically typed language background, I was initially a little hesitant at leaping into the dynamically typed world.

The journey has been quite a transformation. The compiler used to give me a feeling of safety, a little more confidence that the code I had written actually worked. Now, my unit and integration tests give me far more confidence than I’ve ever had. The compiler sometimes feels like an unnecessary burden, a constant reminder to speak and behave in a strict and formal manner at all times.

Dynamic languages give you more freedom to express your intent clearly and concisely, uncluttered by the overhead of needing to satisfy a compiler. JavaScript is a fantastic language and is now practically ubiquitous on the web. If JavaScript is one of those languages you have dabbled in but not really learned in depth, spend some time to really learn it. Buy Pro JavaScript Techniques by John Resig (creator of jQuery), check out Knockout.js and start writing those pure JavaScript apps. You won’t look back!

Closures

One language feature that learning JavaScript helped me to grok is that of a closure, also known as a lexical closure, function closure, function value or functional value. Closures enable certain patterns to be expressed more succinctly and can significantly reduce the amount of code required to maintain state. They have also been available in C# since .NET 3.5 and the introduction of anonymous methods.

So what is a closure? Wikipedia defines it as:

“a function together with a referencing environment for the non-local variables of that function.[1] A closure allows a function to access variables outside its typical scope.”

Whoa. I’m not going to try to explain that in English.

Instead, we’re going to look at a simple example involving a wrapper around the FileSystemWatcher class that spawns a thread and executes a delegate on each file change.

Let’s have a stab in the dark.

public class BasicFileWatcher
{
    private string _path;
    private Action<string, WatcherChangeTypes> _action;
    private WatcherChangeTypes _changeTypes = WatcherChangeTypes.All;
    private FileSystemWatcher _fileSystemWatcher;

    public void Watch(string path, Action<string, WatcherChangeTypes> action, 
                        string filter, WatcherChangeTypes changeTypes)
    {
        _path = path;
        _action = action;
        _changeTypes = changeTypes;
        _fileSystemWatcher = new FileSystemWatcher(path, filter);

        new Thread(Wait).Start();
    }

    private void Wait()
    {
        while (true)
        {
            var change = _fileSystemWatcher.WaitForChanged(_changeTypes);
            _action(Path.Combine(_path, change.Name), change.ChangeType);
        }
    }
}

Hmm… That pesky ThreadStart delegate doesn’t have any parameters. This means we need to store a stack of state that the Wait method requires. If only there was a different way of passing that state… (forget about the loosely typed ParameterizedThreadStart delegate)

public class ClosureFileWatcher
{
    public void Watch(string path, Action<string, WatcherChangeTypes> action, 
                        string filter, WatcherChangeTypes changeTypes)
    {
        var fileSystemWatcher = new FileSystemWatcher(path, filter);

        new Thread(() => {
                             while (true)
                             {
                                 var change = fileSystemWatcher.WaitForChanged(changeTypes);
                                 action(Path.Combine(path, change.Name), change.ChangeType);
                             }
                         }).Start();
    }
}

That’s a bit nicer! So what did we do here?

Instead of passing a reference to the Wait function, we passed an anonymous function in the form of a lambda expression containing the same code. This creates a closure – the anonymous function has access to variables in the parent scope (the “referencing environment containing non-local variables”).

Neat. A simple console app to test this:

static void Main(string[] args)
{
    new ClosureFileWatcher().Watch(
            path: @"C:\temp",
            filter: "*.txt",
            changeTypes: WatcherChangeTypes.All,
            action: (path, type) => Console.WriteLine("{0}: {1}", type.ToString(), path));
}

There is a consequence to be aware of. On creation of the closure, a reference is created from the inner function to the outer function, effectively extending the lifetime of the containing object to that of the anonymous function. Such functions containing long running processes will cause the containing object to remain in memory as well.

See ya next time!

0 Comments:

Post a Comment

<< Home