Generics are often introduced as the mainstream language feature of Whidbey (.NET2). However, while surfing off the beaten path I realized that new features named anonymous methods and iterators are also very interesting. Unlike generics, these two features don't imply new IL instructions set changes compared to .NET1 IL instructions set or any CTS changes. All the magic is in the compilers. This article is the first of a series of two articles dedicated to these new features. While these articles unfold, you will understand the reasons for beginning with anonymous methods. Both articles share the same structure: first a basic introduction to the functionality, followed by a strong analysis of the compiler work before drilling into advanced uses.
width="300" scrolling="no" height="250" frameborder="0" marginwidth="0" marginheight="0" border="0" src="http://ad.doubleclick.net/adi/atssn/tt;bkg=FFFFFF;kw=;dcopt=;pos=1;sz=300x250;ptile=1;ord=8192936?"> <a href='http://ad.doubleclick.net/jump/atssn/tt;bkg=FFFFFF;kw=;dcopt=;pos=1;sz=300x250;ptile=1;ord=8192936?''> <img src='http://ad.doubleclick.net/ad/atssn/tt;bkg=FFFFFF;kw=;dcopt=;pos=1;sz=300x250;ptile=1;ord=8192936?'' width='300' height='250' border='0'></a> |
Let's begin by enhancing some C# v1 code to use C# v2 anonymous methods. Here is a simple C# v1 program that first references and then invokes a method, through a delegate.
System;
{
();
GetMethod(){
(MethodBody);
}
MethodBody(){
.WriteLine();
}
Main(){
delegateInstance = GetMethod();
delegateInstance();
delegateInstance();
.ReadKey();
}
}
Here is the same program rewritten with a C# v2 anonymous method:
System;
{
();
GetMethod(){
(){ .WriteLine();};
}
Main(){
delegateInstance = GetMethod();
delegateInstance();
delegateInstance();
.ReadKey();
}
}
You should notice that:
You should notice as well that it is possible to use the operator += to allow a delegate instance to reference several methods (anonymous or not):
System;
{
();
static void Main(){
delegateInstance = () { .WriteLine(); };
delegateInstance += () { .WriteLine(); };
delegateInstance();
.ReadKey();
}
}
As you might expect this program outputs:
Hello
Bonjours
As shown in the following example, an anonymous method can accept some arguments of any type. You can also use keywords ref and out to tune how arguments are passed:
System;
{
( valTypeParam, refTypeParam, refParam, outParam);
GetMethod(){
( valTypeParam, refTypeParam, refParam, outParam)
{
.WriteLine(, valTypeParam, refTypeParam);
refParam++;
outParam = 9;
valTypeParam;
};
}
Main(){
delegateInstance = GetMethod();
refVar = 5;
outVar;
i = delegateInstance(1, , refVar, outVar);
j = delegateInstance(2, , refVar, outVar);
.WriteLine(, i, j, refVar, outVar);
.ReadKey();
}
}
This program outputs:
Hello valParam:1 refTypeParam:one
Hello valParam:2 refTypeParam:two
i:1 j:2 refVar:7 outVar:9
As you can see, the returned type is not defined inside the anonymous method declaration. The returned type of an anonymous method is inferred by the C# v2 compiler from the returned type of the delegate to which it is assigned. This type is always known because the compiler forces assignment of any anonymous method to a delegate.
An anonymous method can't be tagged with an attribute. This restriction implies that you can't use the param keyword in the list of arguments of an anonymous method. Indeed, using the keyword param forces the compiler to tag the concerned method with the ParamArray attribute.
System;
{
( [] arr);
GetMethod(){
( [] arr){
.WriteLine();
};
}
}
It is possible to declare an anonymous method without any signature, i.e. you are not compelled to write a pair of parenthesis after the keyword delegate if your anonymous method doesn't take any argument. In this case, your method can be assigned to any delegate instance that returns a void type and that doesn't have an out argument. Obviously, such an anonymous method doesn't have access to parameters that are provided through its delegate invocation.
System;
{
( valTypeParam, refTypeParam, refParam);
Main(){
delegateInstance = { .WriteLine();};
refVar = 5;
delegateInstance(1, , refVar);
delegateInstance(2, , refVar);
.ReadKey();
}
}
As shown in the example below, an argument of an anonymous method can have a generic type:
System;
<T>{
(T t);
UneMethode(T t){
Type Instance = (T arg){ .WriteLine(,arg.ToString());};
Instance(t);
}
}
{
Main(){
<> inst = <>();
inst.UneMethode(5.5);
.ReadKey();
}
}
In.NET v2, a delegate type can be declared with some generics arguments. An anonymous method can be assigned to a delegate instance of such a type. You just have to resolve type parameters on both side of the assignment:
System;
{
(T t);
static void Main(){
<> Instance = ( arg) { .WriteLine(, arg.ToString()); };
Instance(5.5);
.ReadKey();
}
}
Anonymous methods are particularly suited to define ‘small' methods that must be invoked ‘by design' through a delegate. For instance, you might use an anonymous method to code the entry point procedure of a thread:
System;
System.Threading;
{
Main(){
thread = ((){
.WriteLine(,.CurrentThread.GetHashCode());
});
thread.Start();
.WriteLine(, .CurrentThread.GetHashCode());
.ReadKey();
}
}
This program outputs:
ThreadHashCode:1 Bonjour
ThreadHashCode:3 Hello
Another classic example of this kind of use lies in the winform's controls callback procedures:
: Form{
Button m_Button;
(){
InitializeComponent();
m_Button.Click += ( sender, EventArgs args){
MessageBox.Show();
};
}
InitializeComponent()
{...}
}
It seems that anonymous method looks like a tiny language enhancement. It's now time to dig under the hood to realize that anonymous methods are far more complex and can be far more useful.
The easy way
As you might expect, when an anonymous method is compiled, an extra method is spawned by the compiler in the concerned class.
System;
{
();
Main(){
delegateInstance = () { .WriteLine(); };
delegateInstance();
.ReadKey();
}
}
Thus, the following assembly is the compiled version of the previous program (the assembly is viewed with the tool Reflector provided freely by Lutz Roeder. Reflector supports right now .NET2 assemblies). :
Indeed, a new private and static method named
We also note that a delegate field named <>9_CachedAnonymousMethoddelegate1 of type delegateType has been generated to reference our anonymous method.
It is interesting to note that all these generated members can't be viewed with the C# intellisense because their names contain a pair of angle brackets < >. Such names are valid for the CLR syntax but incorrect for the C# syntax.
To keep things clear and simple, we haven't mentioned yet the fact that an anonymous method can have access to a local variable of its outer method. Let's analyze this possibility through the following example:
System;
{
();
MakeCounter(){
counter = 0;
delegateInstanceCounter = { ++counter; };
delegateInstanceCounter;
}
Main(){
TypeCounter counter1 = MakeCounter();
TypeCounter counter2 = MakeCounter();
.WriteLine(counter1());
.WriteLine(counter1());
.WriteLine(counter2());
.WriteLine(counter2());
.ReadLine();
}
}
This program outputs:
1
2
1
2
Think about it, it might stump you:
Note that in .NET 2005, the CLR and the IL language haven't been tweaked to support the anonymous method feature. The mystery might stems from the compiler. It's a nice example of ‘syntactic sugar'. Let's analyse the assembly:
This analysis makes things clear because:
Before explaining why the compiler has this surprising behaviour, let's go further to get a thorough understanding of its work.
The following subtle example has been mentioned in the blog of Brad Abrams.
System;
System.Threading;
{
Main([] args){
( i = 0; i < 5; i++)
.QueueUserWorkItem( { .WriteLine(i); }, );
.ReadLine();
}
}
This program outputs in a non-deterministic way something like:
0
1
5
5
5
This result compels us to infer that the local variable i is shared amongst all threads. The execution is non-deterministic because the Main() method and our closure are executed simultaneously by several threads. To make things clear, here is the decompiled code of the Main() method:
Main([] args){
flag1;
.<>c__DisplayClass1 class1 = .<>c__DisplayClass1();
class1.i = 0;
Label_0030;
Label_000F:
.QueueUserWorkItem( (class1.b__0) , );
class1.i++;
Label_0030:
flag1 = class1.i < 5;
(flag1){
Label_000F;
}
.ReadLine();
}
Notice that the fact that the value 5 is printed indicates that the Main() method is out of the loop at the moment of printing.
The following version of this program has a deterministic execution:
System;
System.Threading;
{
Main([] args){
for ( i = 0; i < 5; i++){
j = i;
.QueueUserWorkItem( { .WriteLine(j); }, );
}
.ReadLine();
}
}
This time, the program outputs:
0
1
2
3
4
This behavior stems from the fact that the local variable j is captured for each iteration. Here is the decompiled code of the Main() method:
Main([] args){
.<>c__DisplayClass1 class1;
flag1;
num1 = 0;
Label_0029;
Label_0004:
class1 = .<>c__DisplayClass1();
class1.j = num1;
.QueueUserWorkItem( (class1.b__0), );
num1++;
Label_0029:
flag1 = num1 < 5;
(flag1){
Label_0004;
}
.ReadLine();
}
This sheds light on the fact that capturing local variables with anonymous methods is not an easy thing. You should always take care when using this feature.
Note that a captured local variable is no longer a local variable. If you access such a variable with some unsafe code, you might have fixed it before (with the C# keyword fixed).
Arguments of a method can always be deemed as local variables. Therefore, C# v2 allows an anonymous method to use arguments of its outer method. For instance:
System;
{
();
MakeCounter( counterName){
counter = 0;
delegateInstanceCounter = {
.WriteLine(counterName + (++counter).ToString());
};
delegateInstanceCounter;
}
Main(){
counterA = MakeCounter();
counterB = MakeCounter();
counterA();
counterA();
counterB();
counterB();
.ReadLine();
}
}
This program outputs:
Counter A:1
Counter A:2
Counter B:1
Counter B:2
Nevertheless, an anonymous method can't capture an out or ref argument. This restriction is readily understandable as soon as you realize that such an argument can't be seen as a local variable. Indeed, such an argument survives the execution of the method.
An anonymous method can access members of its outer class. The case of static member's access is readily understandable since there is one and only one occurrence of any static field in the domain application. Thus, there is nothing like ‘capturing' a static field.
An instance member's access is less obvious. To clarify this point, remember that the this reference that allows access to instance members, is a local variable of the outer instance method. Therefore, the this reference is captured by the anonymous method. Let's analyze the following example:
System;
();
{
m_Name;
( name) { m_Name = name; }
MakeCounter( counterName){
counter = 0;
delegateInstanceCounter = {
.Write(counterName +(++counter).ToString());
.WriteLine( + m_Name); // on aurait pu écrire this.m_Name
};
DelegateInstanceCounter;
}
}
{
Main(){
counterMaker1 = ();
counterMaker2 = ();
counterA = counterMaker1.MakeCounter();
counterB = counterMaker1.MakeCounter();
counterC = counterMaker2.MakeCounter();
counterA(); counterA();
counterB(); counterB();
counterC(); counterC();
.ReadLine();
}
}
This program outputs:
Counter A:1 Counter built by:Factory1
Counter A:2 Counter built by:Factory1
Counter B:1 Counter built by:Factory1
Counter B:2 Counter built by:Factory1
Counter C:1 Counter built by:Factory2
Counter C:2 Counter built by:Factory2
Let's decompile the MakeCounter() method to expose the this reference capture:
internal MakeCounter( counterName){
.<>c__DisplayClass1 class1 = .<>c__DisplayClass1();
class1.<>4__this = ;
class1.counterName = counterName;
class1.counter = 0;
(class1.b__0);
}
Notice that the this reference cannot be captured by an anonymous method that is defined in a structure. Here is the compiler error:
A closure is a function that captures values of its lexical environment, when it is created at run-time. The lexical environment of a function is the set of variables visible from the concerned function.
In previous definitions, we used carefully the terms when and from. It indicates that the notion of closure pinpoints something that exists at run-time (as the concept of object). It indicates also that the notion of lexical environment pinpoints something that exists in the code, i.e at compile-time (as the concept of class). Consequently, you can consider that the lexical environment of a C# v2 anonymous method is the class generated by the compiler. Following the same idea, you can consider that an instance of such a generated class is a closure.
The definition of closure also implies the notion of creating a function at run-time. Mainstream imperative languages such as C, C++, C#1, Java or VB.NET1 don't support the ability to create an instance of a function at run-time. This feature stems from functional languages such as Haskell or Lisp. Thus C# v2 goes beyond imperative languages by supporting closures. However, C# v2 is not the first imperative language that supports closures since Perl and Ruby also have this feature.
A function computes its results both from values of its arguments and from the context that surrounds its invocation. You can consider this context as a set of background data. Thus, arguments of a function can be seen as foreground data. Therefore, the decision that an input data of a function must be an argument must be taken from the relevance of the argument for the computation.
Generally, when using object languages, the context of a function (i.e. the context of an instance method) is the state of the object on which it is invoked. When programming with non object oriented imperative languages such as C, the context of a function is the values of global variables. When dealing with closures, the context is the values of captured variables when the closure is created. Therefore, as classes, closures are a way to associate behavior and data. In object oriented world, methods and data are associated thanks to the this reference. In functional world a function is associated with the values of captured variables. To make thinks clear:
The previous section implicitly implies that some sort of classes could be replaced by some anonymous methods. Actually, we already perform such replacement in our implementation of counter. The behavior is the increment of the counter while the state is its value. However, the counter implementation doesn't harness the possibility to pass argument to an anonymous method. The following example shows how to harness closures to perform parameterized computation on the state of an object:
System;
{
( integerToMultiply);
BuildMultiplier( multiplierParam){
( integerToMultiply){
egerToMultiply *= multiplierParam;
};
}
Main(){
multiplierPar8 = BuildMultiplier(8);
multiplierPar2 = BuildMultiplier(2);
aninteger = 3;
multiplierPar8( aninteger);
multiplierPar2( aninteger);
.ReadLine();
}
}
Here is another example that shows how to harness closures to perform parameterized computation to obtain a value from the state of an object:
System;
{
( price) { m_Price = price; }
m_Price;
Price { { m_Price; } }
}
class Program{
( article);
static BuildTaxComputer( tva){
( article){
(article.Price * (100 + tva)) / 100;
};
}
static void Main(){
taxComputer19_6 = BuildTaxComputer(19.6m);
taxComputer5_5 = BuildTaxComputer(5.5m);
article = (97);
.WriteLine("Price (Tax=19.6%) : "+ taxComputer19_6(article));
.WriteLine("Price (Tax=5.5%) : "+ taxComputer5_5(article));
.ReadLine();
}
}
For those who have fiddled with the C++'s Standard Template Library (STL), functors as you might recall are an elegant programming style that allows you to tweak a collection in any way with a single line of code. The good news is that anonymous methods make this feature a reality in the .NET world.
The name functor stands for function-object. Basically, a functor is a parameterized behaviour. Functors are particularly suited to manipulate collections because you can apply its behaviour to all elements of a collection with a single line of code. For instance, suppose that you have a list of articles and that you want to select articles that cost more than a given price. You would write:
<> SelectExpensiveArticle( expensiveThreshold, <> listIn){
<> listOut = <>();
( article listIn)
( article.Price > expensiveThreashold )
listOut.Add(article);
listOut;
}
With C# v2's functors you can code the same behaviour like this:
<> SelectExpensiveArticle2( expensiveThreshold,
<> listIn){
listIn.FindAll(( article) { (article.Price >
expensiveThreashold); });
}
Functors are good at four things:
Here is a small program that illustrates these functionalities:
System.Collections.Generic;
{
{
( price, name){Price = price;Name = name;}
Price;
Name;
}
Main(){
List<> egers = List<>();
( i=1; i<=10; i++) egers.Add(i);
<> articles = <>();
articles.Add( (23,));
articles.Add( (56,));
List<> even = integers.FindAll(( i){ i%2==0; });
sum = 0;
integers.ForEach(( i) { sum += i; });
increment = 10;
articles.ForEach(( x) { x.Price += increment; });
articles.Sort(( x, y){
<>.Default.Compare(x.Price,y.Price); });
System.Converter(T from)
<> articlesPrices = articles.ConvertAll<>(
( article) { ()article.Price; });
System..ReadKey();
}
}
new, Courier, mono">System.Collections.Generic.List
public class: System.Collections.Generic. ,
System.Collections.Generic.,
System.Collections.Generic., System.Collections.,
System.Collections., System.Collections.{
FindIndex(Predicatematch);
FindIndex( index, Predicatematch);
FindIndex( index, count, Predicatematch);
FindLastIndex(Predicatematch);
FindLastIndex( index, Predicatematch);
FindLastIndex( index, count, Predicatematch);
ListFindAll(Predicate match);
T Find(Predicatematch);
T FindLast(Predicate match);
Exists(Predicatematch);
TrueForAll(Predicatematch);
RemoveAll(Predicatematch);
ForEach(Actionaction);
Sort(ComparisonComparison);
public List ConvertAll(Converterconverter);
...
}
public class {
FindIndex(T[] array, startIndex,
count, Predicatematch);
FindIndex(T[] array, startIndex,
Predicatematch);
FindIndex(T[] array, Predicate match);
FindLastIndex(T[] array, startIndex,
count, Predicatematch);
FindLastIndex(T[] array, startIndex,
Predicatematch);
FindLastIndex(T[] array, Predicate match);
T[] FindAll(T[] array, Predicate match);
T Find(T[] array, Predicate match);
T FindLast(T[] array, Predicate match);
Exists(T[] array, Predicate match);
TrueForAll(T[] array, Predicate match);
ForEach(T[] array, Action action);
Sort(T[] array, System. comparison);
U[] ConvertAll(T[] array, Converter converter);
...
}
After going through the basics of anonymous methods, we discovered that this feature is more complex and more useful than expected at first glance. This new C# v2 feature is an implementation of the notion of closures of functional languages. In C# v2, closures can be seen as some syntactic sugar and understanding the work of the compiler is essential. We saw that using closures incorrectly can be harmful for code clarity. We saw that closures are a good means to replace some small classes. They can also dramatically enhance list manipulation.
In a next article we'll cover iterators of C# v2. We'll underline the work of the compiler in order to use properly this feature.
The C# programming language by Anders Hejlsberg, Scott Wiltamuth, Peter Golde
Create Elegant Code with Anonymous Methods, Iterators, and Partial Classes by Juval Lowy
Implementation of Closures (Anonymous Methods) in C# 2.0 (Part 6) Roshan James's blog
Closures in CLR 2.0 Antonio Cisternino's blogs
Fun with Anonymous Methods Brad Abrams's blog
What is closure c2 Wiki
Anonymous Methods c2 Wiki
Anonymous Methods, Part 2 of ? GrantRi's WebLog [MS]
Charming Python: Functional programming in Python, Part 2 by David Mertz
Patrick Smacchia is a .NET MVP involved in software development for over 15 years. After graduating in mathematics and computer science from the ENSEEIHT school, he has worked on software in a variety of fields including stock exchange at Société Générale, airline ticket reservation system at Amadeus as well as a satellite base station at Alcatel. He's currently a software consultant and trainer on .NET technologies as well as the author of the freeware NDepend which provides numerous metrics and caveats on any compiled .NET application. |