C# 2.0 (released in
November 2005) introduced a new feature, anonymous methods, that allows
you to declare your method code inline instead of with a delegate
function. Lambda expressions, a new feature in C# 3.0, have a more
concise syntax to achieve the same goal. Take a closer look at
anonymous methods before discussing lambda expressions.
Suppose you want to create a button that updates a ListBox on your
form when you click it. In C# 1.0 and 1.1, you would do it as follows:
public MyForm()
{
listBox = new ListBox(...);
textBox = new TextBox(...);
addButton = new Button(...);
addButton.Click += new EventHandler(AddClick);
}
void AddClick(object sender, EventArgs e)
{
listBox.Items.Add(textBox.Text);
}
With C# 2.0, you could have it as under:
public MyForm()
{
listBox = new ListBox(...);
textBox = new TextBox(...);
addButton = new Button(...);
addButton.Click += delegate
{
listBox.Items.Add(textBox.Text);
};
As you can see, you don't have to explicitly declare a new method to
link it with an event. You can use anonymous methods to achieve the
same thing in C# 2.0. C# 3.0 introduces an even simpler syntax, lambda
expressions, which you write as a parameter list followed by the "=>" token, followed by an expression or a statement block.
Parameters to Lambda Expressions
The parameters of a lambda expression can be explicitly or
implicitly typed. In an explicitly typed parameter list, the type of
each expression is explicitly specified. In an implicitly typed
parameter list, the types are inferred from the context in which the
lambda expression occurs:
(int x) => x + 1 // explicitly typed parameter
(y,z) => return y * z; // implicitly typed parameter
Lambda Expressions Demonstration
The following code snippet from the downloadable source code uses two techniques to print out strings in a list whose length is even. The first technique, implemented in the function AnonMethod, is through anonymous methods. The second technique, implemented in the function LambdaExample, is through a lambda expression:
// Program.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Query;
using System.Xml.XLinq;
using System.Data.DLinq;
namespace LambdaExample
{
public delegate bool KeyValueFilter<K, V>(K key, V value);
static class Program
{
static void Main(string[] args)
{
List<string> list = new List<string>();
list.Add("AA");
list.Add("ABC");
list.Add("DEFG");
list.Add("XYZ");
Console.WriteLine("Through Anonymous method");
AnonMethod(list);
Console.WriteLine("Through Lambda expression");
LambdaExample(list);
Dictionary<string, int> varClothes= new Dictionary<string,int>();
varClothes.Add("Jeans", 20);
varClothes.Add("Shirts", 15);
varClothes.Add("Pajamas", 9);
varClothes.Add("Shoes", 9);
var ClothesListShortage = varClothes.FilterBy((string name,
int count) => name == "Shoes" && count < 10);
// example of multiple parameters
if(ClothesListShortage.Count > 0)
Console.WriteLine("We are short of shoes");
Console.ReadLine();
}
static void AnonMethod(List<string> list)
{
List<string> evenNumbers =
list.FindAll(delegate(string i)
{ return (i.Length % 2) == 0; });
foreach (string evenNumber in evenNumbers)
{
Console.WriteLine(evenNumber);
}
}
static void LambdaExample(List<string> list)
{
var evenNumbers = list.FindAll(i =>
(i.Length % 2) == 0); // example of single parameter
foreach(string i in evenNumbers)
{
Console.WriteLine(i);
}
}
}
public static class Extensions
{
public static Dictionary<K, V> FilterBy<K, V>
(this Dictionary<K, V> items, KeyValueFilter<K, V> filter)
{
var result = new Dictionary<K, V>();
foreach(KeyValuePair<K, V> element in items)
{
if (filter(element.Key, element.Value))
result.Add(element.Key, element.Value);
}
return result;
}
}
}
The second technique is sort of interesting. It filters the list of
strings based on the length of the string, but it uses a new syntax. If
you have Visual Studio 2005 and LinQ Preview
installed, you can use the editor to compile the application. If you do
not have VS2005, you can compile the code from the command line by
using the following command:
C:\Program Files\LINQ Preview\Bin\Csc.exe
/reference:"C:\Program Files\LINQ Preview\Bin\System.Data.DLinq.dll"
/reference:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Data.dll
/reference:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.dll
/reference:"C:\Program Files\LINQ Preview\Bin\System.Query.dll"
/reference:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Xml.dll
/reference:"C:\Program Files\LINQ Preview\Bin\System.Xml.XLinq.dll"
/target:exe Program.cs
The New Lambda Expressions Feature in C# 3.0
By Vipul Patel
Go to page:
Prev 1 2
Inside the Intermediate Language (IL)
To get under the
hood of the code, fire up ILDASM and select the application. You will
see something similar to the screenshot in Figure 1.
Click here for a larger image.
Figure 1: Sample Application in ILDASM
Double-click the AnonMethod function to see the IL generated by the C# compiler:
.method private hidebysig static void AnonMethod(class
[mscorlib]System.Collections.Generic.List`1<string> list)
cil managed
{
// Code size 96 (0x60)
.maxstack 4
.locals init ([0] class [mscorlib]System.Collections.Generic.List
`1<string> evenNumbers,
[1] string evenNumber,
[2] valuetype [mscorlib]System.Collections.Generic.List
`1/Enumerator<string> CS$5$0000,
[3] bool CS$4$0001)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldsfld class [mscorlib]System.Predicate
`1<string> LambdaExample.Program::
`<>9__CachedAnonymousMethodDelegate1'
IL_0007: brtrue.s IL_001c
IL_0009: ldnull
IL_000a: ldftn bool LambdaExample.Program::
`<AnonMethod>b__0'(string)
IL_0010: newobj instance void class [mscorlib]System.Predicate
`1<string>::.ctor(object, native int)
IL_0015: stsfld class [mscorlib]System.Predicate`1<string>
LambdaExample.Program::
`<>9__CachedAnonymousMethodDelegate1'
IL_001a: br.s IL_001c
IL_001c: ldsfld class [mscorlib]System.Predicate`1<string>
LambdaExample.Program::'<>
9__CachedAnonymousMethodDelegate1'
IL_0021: callvirt instance class [mscorlib]System.Collections.
Generic.List`1<!0> class [mscorlib]System.
Collections.Generic.List`1<string>::
FindAll(class [mscorlib]System.Predicate`1<!0>)
IL_0026: stloc.0
IL_0027: nop
IL_0028: ldloc.0
IL_0029: callvirt instance valuetype [mscorlib]System.Collections.
Generic.List`1/Enumerator<!0> class
[mscorlib]System.Collections.Generic.List`1
<string>::GetEnumerator()
IL_002e: stloc.2
.try
{
IL_002f: br.s IL_0042
IL_0031: ldloca.s CS$5$0000
IL_0033: call instance !0 valuetype [mscorlib]System.
Collections.Generic.List`1/Enumerator
<string>::get_Current()
IL_0038: stloc.1
IL_0039: nop
IL_003a: ldloc.1
IL_003b: call void [mscorlib]System.Console::
WriteLine(string)
IL_0040: nop
IL_0041: nop
IL_0042: ldloca.s CS$5$0000
IL_0044: call instance bool valuetype [mscorlib]System.
Collections.Generic.List`1/Enumerator
<string>::MoveNext()
IL_0049: stloc.3
IL_004a: ldloc.3
IL_004b: brtrue.s IL_0031
IL_004d: leave.s IL_005e
} // end .try
finally
{
IL_004f: ldloca.s CS$5$0000
IL_0051: constrained. valuetype [mscorlib]System.Collections.
Generic.List`1/Enumerator<string>
IL_0057: callvirt instance void [mscorlib]System.
IDisposable::Dispose()
IL_005c: nop
IL_005d: endfinally
} // end handler
IL_005e: nop
IL_005f: ret
} // end of method Program::AnonMethod
Now see the IL generated by the LambdaExample function. It is strikingly similar to the code generated by the LambdaExample method:
.method private hidebysig static void LambdaExample(class [mscorlib]System.Collections.Generic.List`1<string> list) cil managed
{
// Code size 96 (0x60)
.maxstack 4
.locals init ([0] class [mscorlib]System.Collections.Generic.List`1<string> evenNumbers,
[1] string i,
[2] valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string> CS$5$0000,
[3] bool CS$4$0001)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldsfld class [mscorlib]System.Predicate`1<string> LambdaExample.Program::'<>9__CachedAnonymousMethodDelegate5'
IL_0007: brtrue.s IL_001c
IL_0009: ldnull
IL_000a: ldftn bool LambdaExample.Program::'<LambdaExample>b__4'(string)
IL_0010: newobj instance void class [mscorlib]System.Predicate`1<string>::.ctor(object,
native int)
IL_0015: stsfld class [mscorlib]System.Predicate`1<string> LambdaExample.Program::'<>9__CachedAnonymousMethodDelegate5'
IL_001a: br.s IL_001c
IL_001c: ldsfld class [mscorlib]System.Predicate`1<string> LambdaExample.Program::'<>9__CachedAnonymousMethodDelegate5'
IL_0021: callvirt instance class [mscorlib]System.Collections.Generic.List`1<!0> class [mscorlib]System.Collections.Generic.List`1<string>::FindAll(class [mscorlib]System.Predicate`1<!0>)
IL_0026: stloc.0
IL_0027: nop
IL_0028: ldloc.0
IL_0029: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<string>::GetEnumerator()
IL_002e: stloc.2
.try
{
IL_002f: br.s IL_0042
IL_0031: ldloca.s CS$5$0000
IL_0033: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>::get_Current()
IL_0038: stloc.1
IL_0039: nop
IL_003a: ldloc.1
IL_003b: call void [mscorlib]System.Console::WriteLine(string)
IL_0040: nop
IL_0041: nop
IL_0042: ldloca.s CS$5$0000
IL_0044: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>::MoveNext()
IL_0049: stloc.3
IL_004a: ldloc.3
IL_004b: brtrue.s IL_0031
IL_004d: leave.s IL_005e
} // end .try
finally
{
IL_004f: ldloca.s CS$5$0000
IL_0051: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>
IL_0057: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_005c: nop
IL_005d: endfinally
} // end handler
IL_005e: nop
IL_005f: ret
} // end of method Program::LambdaExample
From the code
listing above, you can deduce that the anonymous method and the lambda
expression essentially compile to the same IL internally. Hence, their
executions are similar.
Lambda Expressions with Multiple Parameters
Lambda expressions can take up multiple parameters. For example, say you have a Dictionary containing the information in Table 1.
Clothing TypeCountShirts15Jeans12Shoes9Pajamas9
Table 1: Information in Dictionary
If you have an anonymous method (FilterBy)
to filter the dictionary by key and value, you can call that anonymous
method through lambda expressions passing multiple parameters. The accompanying source code has the implementation of FilterBy:
var ClothesListShortage = clothesList.FilterBy((string name, int count)
=> name == "Shoes" && count < 10);
Simpler Syntax
Lambda expressions
provide a simple way to write inline code blocks where delegates are
expected. Their behavior is strikingly similar to anonymous methods. In
fact, they are syntactic sugar in terms of syntax. They can take
multiple parameters, explicit or implicit.
Download the Code
You can download the code that accompanies the article here.