Common Type System—Anonymous Delegates


Jump to: navigation, search
CSharp-Online.NET:Articles
.NET Articles

Common Type System

© 2006 Wiley Publishing Inc.

Anonymous Delegates (Language Feature)

This is a feature of the C# 2.0 language, not of the CLR itself. But anonymous delegates are so useful and pervasive that it’s worth a brief mention in this chapter. Due to the ease with which delegates permit you to pass method pointers as arguments to other methods, it’s sometimes preferable to simply write your block of code inline rather than having to set up another method by hand. Anonymous delegates permit you to do this. It’s purely syntactic sugar.

Consider a method that takes a delegate and applies it a number of times:

delegate int IntIntDelegate(int x);
 
void TransformUpTo(IntIntDelegate d, int max)
{
   for (int i = 0; i <= max; i++)
   Console.WriteLine(d(i));
}

If we wanted to pass a function to TransformUpTo that squared the input, we’d have to first write an entirely separate method over which we’d form a delegate. However, in 2.0, we can use anonymous delegates to accomplish the same thing:

TransformUpTo(delegate(int x) { return x * x; }, 10);

The C# compiler generates an anonymous method in your assembly that implements the functionality indicated inside the curly braces. The compiler is smart enough to deduce that the function returns an integer (because it’s used in a context that expected a function returning an integer), and the parameter types are specified explicitly in the parenthesis following the delegate keyword.

We won’t spend too much time on this feature. But suffice it to say, it’s very complex and very powerful. You can capture variables inside the delegate that are lexically visible. The compiler does a lot of work to ensure that this works correctly. Take a look at the IL that gets generated if you’d like to appreciate the work it’s doing for you. Consider this example:

delegate void FooBar();
 
void Foo()
{
   int i = 0;
   Bar(delegate { i++; });
   Console.WriteLine(i);
}
 
void Bar(FooBar d)
{
   d(); d(); d();
}

It shouldn’t come as a surprise that the output of calling Foo is 3. A local variable i is declared in Foo and set initially to 0. Then we create an anonymous delegate that, when invoked, increments i by one. We pass that delegate to the Bar function, which applies it three times.

If you stop to think about what the compiler is doing here, it’s clever and impressive (and perhaps a tad frightening at the same time). The compiler notices that you’ve accessed a local variable from inside your delegate and responds by hoisting the storage into a heap allocated object. The type of this object is auto-generated by the C# compiler and never seen by your code. It then does the magic to ensure that the references to i in the local scope work with the same object as the delegate.

This is quite nice of the compiler to do, but for the performance-conscious readers, you might worry that this feature can be abused. What appears to be local variable access turns out to actually involve an object allocation and at least two levels of indirection. Your concern would not be without justification, but seldom does it pay off to worry about such micro-performance tuning.


Previous_Page_.gif Next_Page_.gif


Personal tools