The New Lambda Expressions Feature in C# 3.0
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 Type | Count |
---|---|
Shirts | 15 |
Jeans | 12 |
Shoes | 9 |
Pajamas | 9 |
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.
About the Author
Vipul Patel is a Microsoft MVP (two years in a row) in Visual C# and currently works at Microsoft through Volt Information Sciences. He specializes in C# and deployment issues. You can reach him at Vipul_d_patel@hotmail.com.
Page 2 of 2