Monday, June 18, 2007

With the release of .NET Framework 2.0, we have gained access to a very powerful feature called Anonymous Methods. I'm sure that almost any .NET developer has used Anonymous Method at least once and most of us use them on a daily basis.

Typically you use Anonymous Methods when you want to declare an event handler inline, without the need of creating a separate, named method (hence the name Anonymous). Other usage areas include delegates of types such As Predicate or Action. Mentioned delegates are most often used in methods found on all types of collection related classes be it Array or List<T>. Typical code for this may look like this:

string[] urls = GetUrls();
string[] urlWithWww;
urlWithWww = Array.FindAll(urls, delegate(string url) 
{ return url.StartsWith("www"); });

Nothing unusual here. The usefulness of Anonymous Methods comes from the fact that they allow us to access variables defined in the scope in which the very method is defined. Here as an example of how this feature is usually used:

string pattern = ".com";
string[] urls = GetUrls();
string[] urlWithWww;
urlWithWww = Array.FindAll(urls, delegate(string url) 
{ return url.Contains(pattern); });

Great feature indeed, but there is one gotcha that we have to be aware. The problem most often appears when we start to work with multiple threads. Given the following code:

foreach (string url in urls)
{
    DoSomethingAsync(delegate() 
    { 
        Debug.WriteLine(url); 
    });
}

We have to know that the url variable that we pass to a Debug.WriteLine method is "shared" among all Anonymous Methods and because of this, we may (and most probably we will) get results that we do not expect: same url printed few times, than a different url, again printed few times. Confusing, isn't it?

Why is that? Brad Adams explains the mechanism behind this strange behavior here (scroll down to the comments). It's worth mentioning that you could get the same strange behavior without working with multiple threads. It is just far more common to get it while theading.

To make things more "predictable", the code above has to be written in a slightly different way:

for (int i = 0; i < urls.Length; i++)
{
    string url = urls[i];
    DoSomething(delegate() 
    { 
        Debug.WriteLine(url); 
    });
}

Now every Anonymous Method gets its own copy of url and everything works as expected.

kick it on DotNetKicks.com