Search

Sunday, July 30, 2006

Anonymous methods and closures in C#

Cross-posted from http://blogs.msdn.com/abhinaba/archive/2005/08/08/448939.aspx

Anonymous Method

One of the new features in C#2.0 being shipped in with Whidbey is anonymous methods. Though this at first glance looks like an easy way of creating delegates, where instead of having to create a new function which the delegate will call you can pass the code inline.

Before C#2.0

class MyClass
{
delegate void MyDelegate
();
static public void
Function()
{
Console.WriteLine("I'm called"
);
}

static void Main()
{
MyDelegate writeMessage = new MyDelegate
(Function);
writeMessage();
}
}


With C#2.0 and anonymous methods: the Function is eliminated and the same piece of code can be written more elegantly as follows


class MyClass
{
delegate void MyDelegate();
static void Main()
{
MyDelegate writeMessage = delegate ()
{
Console.WriteLine("I'm called"
);
};
writeMessage();
}
}


For more details on Anonymous methods check out http://msdn.microsoft.com/msdnmag/issues/04/05/c20/default.aspx.


However, there is more to it than just getting a short hand way of writing code. With anonymous methods you can now do a lot of things in C# which are commonly done in Functional languages. Consider the code below which is actually a function generator.


class Program
{
delegate void MyDelegate
<T>(T t);
enum
FunctionType
{
WithPrefix,
WithoutPrefix
}
static MyDelegate<T> FunctionGenerator<T>(FunctionType
fType)
{
int i = 0;

if (fType == FunctionType
.WithPrefix)
{
return delegate(T t)
{
Console.WriteLine(i.ToString() + ": "
+ t.ToString());
i++;

};
}
else
{
return delegate
(T t)
{
Console.WriteLine(
t.ToString());
};
}
}


static void Main(string[] args)
{
MyDelegate<int> mdWith = FunctionGenerator<int>(FunctionType
.WithPrefix);
mdWith(5);
mdWith(5);

MyDelegate<string> mdWithout = FunctionGenerator<string>(FunctionType
.WithoutPrefix);
mdWithout(
"Hello"
);
}
}


The output is
0 : 5
1 : 5
Hello


As in the above piece of code you can create Function Generator that actually return different implementations based on requirement. For another interesting sample see Don Box's blog http://pluralsight.com/blogs/dbox/archive/2005/04/17/7467.aspx


Closure


For any languages which allow passing around functions as arguments or functions returning another function the Closure abstraction is very important. For more info on Closure see http://en.wikipedia.org/wiki/Closure_(computer_science. C#2.0 supports closure by the use of anonymous methods.


Closure is a function along with its lexical environment (available variables and their values) when the function is created.


int i = 10;
return delegate(T t)
{
Console.WriteLine(i.ToString() + ": "
+ t.ToString());
};


Since the variable i was available with the value 0 when the Anonymous method was created, inside the method, i will be available with the same value when the method is called via the delegate.


From the definition of Closure it can be inferred that Closure remembers the values of the variables during its creation. However in C# the outer local variable (i in this case) is shared with the anonymous method by creating it on the heap. This means any change to it in the anonymous method changes the original value and when the method is called the second time it gets the modified value of i as 1 (see second line of output). This leads many to argue that anonymous method is not actually a Closure as by its definition the value of a variable at the time of creation of the Closure should be remembered and not modifiable.


Check out the link http://blogs.msdn.com/brada/archive/2004/08/03/207164.aspx on how the implementation of Anonymous methods in C# have led to interesting issues.

No comments: