In This Chapter
Understanding the Evolution from Function Pointers to Lambda Expressions
Writing Basic Lambda Expressions
Dynamic Programming with Lambda Expressions
Lambda Expressions and Closures
Currying
“In wisdom gathered over time I have found that every experience is a form of exploration.”
—Ansel Adams
Hardware capabilities are way ahead of software capabilities, and customer demands are ahead of hardware and software. Holography, instant on and off computers, no moving parts, infinite storage, limitless speed, dependability, and 100% uptime are just a few features that would be great to have.
Businesses and people ask for and need evermore powerful software applications. For programmers, this means code has to do more. Creating code is a function of time and money. Programmers need to be able to use higher and higher levels of abstraction to write code that does more with less and that consumes less time and money. All of this implies code compression—fewer bits of code to do substantially more, hence Lambda Expressions.
Lambda Expressions are based on functional calculus—Lambda Calculus—from the 1930s, and Lambda Expressions exist in other languages. (Check www.wikipedia.org for a brief history of Lambda Calculus.)
Think of Lambda Expressions as brief inline functions whose concise nature is an ideal fit for LINQ. This chapter looks at the evolution path to Lambda Expressions, how to write and use Lambda Expressions, closures, and currying.
In the 1970s Brian Kernighan and Dennis Ritchie brought us function pointers in C (see Listing 5.1). A function is referenced by an address, which is just a number, after all. Function pointers led to the notion of events—really just function pointers—and event handlers. Event handlers evolved into delegates and multicast delegates in .NET (see Listing 5.2). (Remember, fundamentally, we are still talking about function pointers under the hood but with nicer window treatments.) Delegates were compressed into anonymous delegates in .NET 2.0. Anonymous delegates lost some weight by eliminating the function name, return type, and parameter types (shown in Listing 5.3), and anonymous expressions have evolved into Lambda Expressions in Listing 5.4.
// FunctionPointer.cpp : main project file.
#include “stdafx.h”
using namespace System;
typedef void (*FunctionPointer)(System::String ^str);
void HelloWorld(System::String ^str)
{
Console::WriteLine(str);
Console::ReadLine();
}
int main(array<System::String ^> ^args)
{
FunctionPointer fp = HelloWorld;
fp(L”Hello World”);
return 0;
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CSharpFunctionPointer
{
class Program
{
delegate void FunctionPointer(String str);
static void Main(string[] args)
{
FunctionPointer fp = HelloWorld;
fp(“Hello World!”);
}
static void HelloWorld(string str)
{
Console.WriteLine(str);
Console.ReadLine();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CSharpAnonymousDelegate
{
class Program
{
delegate void FunctionPointer(string str);
static void Main(string[] args)
{
FunctionPointer fp = delegate(string s)
{
Console.WriteLine(s);
Console.ReadLine();
};
fp(“Hello World!”);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CSharpAnonymousDelegate
{
class Program
{
delegate void FunctionPointer(string str);
static void Main(string[] args)
{
FunctionPointer fp =
s ⇒ Console.WriteLine(s);
fp(“Hello World!”);
Console.ReadLine();
}
}
}
Using our weight-loss analogy, Lambda Expressions eliminated the delegate
keyword, function header, parameter types, brackets, and return
keyword (see Listing 5.4). This very concise grammar for Lambda Expressions is not plain old esoterrorism (esoteric code for the sole purpose of terrorizing programmers). Lambda Expressions’ concise nature makes it easy for these smallish functions to fit into new constructs that support LINQ.
A Lambda Expression is a concise delegate. The left side (of the ⇒) represents the arguments to the function and the right side is the function body. In Listing 5.4, the Lambda Expression is assigned to the delegate FunctionPointer
, but .NET 3.5 defines anonymous delegates like Action<T>
and Func<T>
, which can be used instead of the more formal, longer delegate statement. This means that you don’t have to define the delegate
statement (as shown in Listing 5.4). Listing 5.5 shows the revision of Listing 5.4, replacing the delegate
statement with the generic Action<T>
delegate type.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LambdaExpressionWithGenericAction
{
class Program
{
static void Main(string[] args)
{
System.Action<string> fp =
s ⇒ Console.WriteLine(s);
fp(“Hello World!”);
Console.ReadLine();
}
}
}
In Listing 5.5, System.Action<T>
is used instead of a custom delegate, as demonstrated in Listing 5.4.
The System
namespace defines generic delegates Action, Func,
and Predicate. Action
performs an operation on the generic arguments. Func
performs an operation on the argument or arguments and returns a value, and Predicate
<T> is used to represent a set of criteria and determine if the argument matches the criteria. Lambda Expressions can be assigned to instances of these types or anonymous types. Or, Lambda Expressions can be used as literal expressions to methods that accept Func, Action,
or Predicate
arguments.
This section looks at examples of Lambda Expressions that match each of these generic delegate types, takes a side trip through automatic properties, and shows ways to use these capabilities within the newer features of .NET.
Automatic properties are not directly or only related to Lambda Expressions, but automatic properties are relevant to the subject of condensed code and letting the framework do more of the labor.
The basic idea behind automatic properties is that many times, programmers define properties that are just wrappers for fields and nothing else happens. Historically, the programmer writes the field declaration, and the property header, getter, and setter, returning the field or setting the field to the value
keyword, respectively. Using properties instead of fields is a recommended practice, but if there are no additional behaviors, automatic properties allow the compiler to add the field, getter, and setter. All the programmer has to do is write the property header.
However, there is a caveat to using automatic properties. You will save some time not writing the whole property and field definition, but you cannot use the field variable in your code. To get around this, you just use the property. Listing 5.6 demonstrates an IronChef
class that has two automatic properties: Name
and Specialty
.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AutomaticProperty
{
class Program
{
static void Main(string[] args)
{
IronChef Batali = new IronChef{
Name=”Mario Batali”, Specialty=”Italian” };
Console.WriteLine(Batali.Name);
Console.ReadLine();
}
}
public class IronChef
{
public string Name{ get; set; };
public string Specialty{ get; set; }
}
}
Listing 5.6 defines the class IronChef
with the automatic properties Name
and Specialty
. Notice that you don’t need to include the property method bodies. Also notice that you don’t need a constructor. With named type initialization (see Chapter 2, “Using Compound Type Initialization”) and automatic properties, simple classes can now be defined with many fewer lines of code and the compiler completes the definition (see the MSIL in Figure 5.1).
Automatic properties implicitly instruct the compiler to generate what is referred to as a backing field and these fields are annotated with the CompilerGeneratedAttribute
.
Lambda Expressions are written with a left side, the ⇒
symbol, and a right side, as in (x,y) ⇒ x + y;
. The left side represents the input arguments and the right side is the expression to be evaluated. For example
(x,y) ⇒ x + y;
is read x
and y
goes to—or as I read it gosinta—the function x + y
. Implicit in the expression is the return result of x + y
. The input argument or arguments can be inferred and generated by the compiler, called implicit arguments, or expressed explicitly by you. The preceding Lambda Expression can also be rewritten as
(int x, int y) ⇒ x + y;
Listing 5.7 shows an example that uses explicit arguments, although doing so is probably just extra typing on your part. The explicit input argument statement is shown in bold.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ExplicitLambdaArguments
{
class Program
{
static void Main(string[] args)
{
Func<int, int, int> add =
(int x, int y) ⇒ x + y;
Console.WriteLine(add(3, 4));
Console.ReadLine();
}
}
}
In Listing 5.7, the generic Func<T,T,TResult>
delegate type is used to represent the Lambda Expression referred to by the variable add
.
The generic Action
delegate lets you capture behavior as an invokable object. Simply provide the parameter types for the Action
, assign it a Lambda Expression, and you are up and running.
Listing 5.8 demonstrates using multiple parameters, including a string
and TextWriter
, which supports sending output to anything that is an instance of a TextWriter
, including the Console
. In Listing 5.9, the Array.ForEach<T>
generic method is demonstrated, accepting an Action<T>
, which supports iterating over an Array<T>
of objects.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;
namespace LambdaExpressionAction
{
class Program
{
static void Main(string[] args)
{
Action<string, TextWriter> print =
(s, writer) ⇒ writer.WriteLine(s);
print(“Console”, Console.Out);
StreamWriter stream = File.AppendText(“c:\temp\text.txt”);
stream.AutoFlush = true;
print(“File”, stream);
Console.ReadLine();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ForEachWithActionLambda
{
class Program
{
static void Main(string[] args)
{
Action<double> print =
amount ⇒ Console.WriteLine(“{0:c}”, amount);
Action<double> michiganSalesTax =
amount ⇒ print(amount *= 1.06);
var amounts = new double[]{
10.36, 12.00, 134};
Array.ForEach<double>(amounts, michiganSalesTax);
Console.ReadLine();
}
}
}
In Listing 5.9, print
uses a Lambda Expression to send a double to the Console
formatted as a currency value. The Lambda Expression represented by michiganSalesTax
adds 6% to the input double and sends the result to print. Near the end of Listing 5.9, the Array.ForEach
generic method is used to iterate over each of the amounts and call michiganSalesTax
. (It would sure be nice if those tax numbers went southward once in a while.)
If you piece some of the elements from prior chapters together, it is easy to see how the mosaic of progression has evolved to LINQ queries. So far, you have learned about extension methods, anonymous types, and, now, Lambda Expressions (among other things). For instance, you know that you can use array initializer syntax and assign the results to an anonymous type, letting the compiler generate the details. Further, you know that arrays implement IEnumerable
and you can add behaviors via extension methods. From Chapter 3, “Defining Extension and Partial Methods,” you know that IEnumerable
has been extended to include generic methods such as Where<T>
that accept predicates—much like WHERE
in Structured Query Language (SQL)—and that these predicates can be expressed as Func<TSource,TResult>
return types or literal Lambda Expressions.
Combining all of this knowledge, you can use Lambda Expressions as arguments to Where<T>
and define short-and-sweet searches, for example, with a very few lines of code. Listing 5.10 demonstrates an anonymous type initialized by an array initializer, use of the Where<T>
with a Lambda Expression predicate to filter the strings (recipes) that have Chicken, and the ForEach
with a second Lambda Expression that writes each found item. This style of programming (in Listing 5.10) is referred to as functional programming.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Searching
{
class Program
{
static void Main(string[] args)
{
var recipes = new[]{
“Crepes Florentine”, “Shrimp Che Paul”,
“Beef Burgundy”, “Rack of Lamb”,
“Tacos”, “Rosemary Roasted Chicken”,
“Juevos Rancheros”, “Buffalo Chicken Nachos”};
var results = recipes.Where(
s⇒s.Contains(“Chicken”));
Array.ForEach<string>(results.ToArray<string>(),
s ⇒ Console.WriteLine(s));
Console.ReadLine();
}
}
}
Listing 5.11 provides a variation on the search behavior by finding odd numbers in a short Fibonacci sequence. This version uses a generic
List<int>
, a Lambda Expression assigned to a Predicate<int>
initialized by a Lambda Expression, and the List<T>.FindAll
method—that accepts a predicate. The results are displayed by the ObjectDumper. ObjectDumper
was implemented as demo code for Visual Basic (VB) but might or might not ship with .NET 3.5. (The ObjectDumper
is installed with sample code at C:Program FilesMicrosoft Visual Studio 9.0Samples1033CSharpSamples.zip.)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace FindingNumbers
{
class Program
{
static void Main(string[] args)
{
List<int> fiboList = new List<int>();
fiboList.AddRange(
new[]{1,1,2,3,5,8,13,21,34,55});
Predicate<int> match = n ⇒ n % 2 == 1;
var odds = fiboList.FindAll(match);
ObjectDumper.Write(odds);
Console.ReadLine();
}
}
}
The Predicate<T>
generic delegate accepts an argument indicated by the parameter T
and returns a Boolean. Predicate<T>
is used in functions like Array.Find
and Array.FindAll.Predicate<T>
can be initialized with a regular function, an anonymous delegate, or a Lambda Expression.
In the example in Listing 5.12 (based on a Windows Form project named FindSquares), the code draws a hundred random rectangles and then uses a Lambda Expression assigned to Predicate<Rectangle>
and only draws the rectangles whose area is less than or equal to a target range. Originally, a hundred rectangles are created and a Timer
paints some number of rectangles every two and a half seconds. The Lambda Expression is on the following line:
Predicate<Rectangle> area = rect ⇒ (rect.Width * rect.Height) <= random.Next(200)
*
random.Next(200));
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace FindSquares
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
Rectangle[] rects = new Rectangle[100];
private void Form1_Load(object sender, EventArgs e)
{
Random random = new Random(DateTime.Now.Millisecond);
int x, y, width, height;
for(int i=0; i<100; i++)
{
x = random.Next(this.ClientRectangle.Width / 2);
y = random.Next(this.ClientRectangle.Height / 2);
width = random.Next(200);
height = random.Next(200);
rects[i] = new Rectangle(x, y, width, height);
}
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
Random random = new Random(DateTime.Now.Millisecond);
Predicate<Rectangle> area = rect ⇒
(rect.Width * rect.Height) <= (random.Next(200) *
random.Next(200));
Rectangle[] matches = Array.FindAll(rects, area);
e.Graphics.Clear(this.BackColor);
foreach(var rect in matches)
e.Graphics.DrawRectangle(Pens.Red, rect);
e.Graphics.DrawString(“Found:” + matches.Length.ToString(),
this.Font, Brushes.Black, 10, ClientRectangle.Height - 40);
}
private void timer1_Tick(object sender, EventArgs e)
{
Invalidate();
}
}
}
It is worth remembering that Lambda Expressions are just very short functions, basically inline functions. Therefore, you can assign a Lambda Expression anywhere you would use a function or anonymous delegate. Remember, as is shown in Listing 5.13, the left side of the <=
operator is the inputs and the right side represents the method body. Therefore, to match a typical EventHandler
, you need the left side to have an object and EventArgs
inputs and the right side to do the typical kinds of things you would do with more verbose event handlers.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace LambdaEventHandlers
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
button1.Click += (s, e) ⇒ MessageBox.Show(“Click!”);
}
}
}
You can begin exploring how Lamba Expressions support LINQ queries by seeing how Lambda Expressions are used in extension methods such as Select<T>
, Where<T>
, or OrderBy<T>
. (Of course, more extension methods than these exist, as more capabilities of LINQ queries are built on top of these extension methods, but you get the general idea.)
This section explores Select<T>
, Where<T>
, and OrderBy<T>
. LINQ fundamentals begin in Chapter 6, “Using Standard Query Operators,” and LINQ keywords are explored in that chapter; just remember that underneath those capabilities are extension methods and Lambda Expressions.
Suppose you have an array of integers. You could simulate the SELECT
* behavior of SQL queries by initializing the Select
extension method with n ⇒ n
. n ⇒ n
means that n
is the input and you want to return n
. Listing 5.14 shows a very simple Select
usage. Select
returns an IEnumerable<T>
instance, where T
is the type of object returned by the Lambda Expression.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SelectLambda
{
class Program
{
static void Main(string[] args)
{
var numbers = new int[]{1,2,3,4,5,6};
foreach(var result in numbers.Select(n ⇒ n))
Console.WriteLine(result);
Console.ReadLine();
}
}
}
In the example, the code doesn’t do much more than if you just used the numbers themselves in the foreach
statement. However, you can’t—with just numbers—project a new type. With the Lambda Expression, you can project n
to a new type with named initialization. For example, changing the Lambda Expression to n⇒
Number=n causes the extension method to project (or create) a new anonymous type with a field Number
. By adding a second named type, you could add an additional field that indicates whether the number n
is odd or even. Listing 5.15 shows the revision.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SelectLambda
{
class Program
{
static void Main(string[] args)
{
var numbers = new int[]{1,2,3,4,5,6};
foreach(var result in numbers.Select(n ⇒ new {Number=n, IsEven=n%2==0}))
Console.WriteLine(result);
Console.ReadLine();
}
}
}
The new anonymous type is shown in the snapshot of ILDASM (see Figure 5.2), clearly showing the projected anonymous type and the two fields Number
and IsEven
.
The Where<T>
extension method is employed in scenarios where you would use conditional logic to filter the elements returned in a resultset. Like Select
, Where
returns an IEnumerable<T>
, where T
is defined by the type of the result from the Lambda Expression. Listing 5.16 provides an example of Where
that looks for words that contain capital D and lowercase e among the array of strings.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WhereLambda
{
class Program
{
static void Main(string[] args)
{
var words = new string[]{“Drop”, “Dead”, “Fred”};
IEnumerable<string> hasDAndE =
words.Where(s ⇒ s.Contains(‘D’) && s.Contains(‘e’));
foreach(string s in hasDAndE)
Console.WriteLine(s);
Console.ReadLine();
}
}
}
OrderBy<T>
is the extension method that supports sorting. OrderBy
accepts a Func
generic delegate. The Func
argument can be expressed with a literal Lambda Expression. Listing 5.17 demonstrates OrderBy
, which is used to sort the array of numbers in ascending order. (To sort in descending order, call the OrderByDescending
method.)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace OrderByLambda
{
class Program
{
static void Main(string[] args)
{
var numbers = new int[]
{1, 3, 5, 7, 9, 2, 4, 6, 8, 10 };
IEnumerable<int> ordered = numbers.OrderBy(n⇒n);
foreach(var num in ordered)
Console.WriteLine(num);
Console.ReadLine();
}
}
}
Lambda Expressions are either compiled as code or data. When a Lambda Expression is assigned to a variable, field, or delegate, the compiler emits executable IL. For example, num ⇒ num % 2 == 0
; emits IL (see Listing 5.18) that is equivalent to a function that accepts an integer, performs division, and compares the remainder with 0. (The Lambda Expression is defined in a function named Test
, hence the emitted <Test>b__1’(int32 num
) generated name.)
.method private hidebysig static bool ‘<Test>b__1’(int32 num) cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.
→CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldc.i4.2
IL_0002: rem
IL_0003: ldc.i4.0
IL_0004: ceq
IL_0006: ret
} // end of method Program::‘<Test>b__1’
If a Lambda Expression is assigned to a variable, field, or parameter whose type is System.Linq.Expressions.Expression<TDelegate>
, where TDelegate
is a delegate, code that represents an expression tree is emitted (see Listing 5.19). Expression trees are used for LINQ features like LINQ for Data where LINQ queries are converted to T-SQL.
.method private hidebysig static void Test2() cil managed
{
// Code size 93 (0x5d)
.maxstack 4
.locals init ([0] class [System.Core]System.Linq.Expressions.ParameterExpression
→CS$0$0000,
[1] class [System.Core]System.Linq.Expressions.ParameterExpression[]
→CS$0$0001)
IL_0000: ldtoken [mscorlib]System.Int32
IL_0005: call class [mscorlib]System.Type [mscorlib]System.Type::
→GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_000a: ldstr “num”
IL_000f: call class [System.Core]System.Linq.Expressions.
→ParameterExpression [System.Core]System.Linq.Expressions.
→Expression::Parameter(class [mscorlib]System.Type,
string)
IL_0014: stloc.0
IL_0015: ldloc.0
IL_0016: ldc.i4.2
IL_0017: box [mscorlib]System.Int32
IL_001c: ldtoken [mscorlib]System.Int32
IL_0021: call class [mscorlib]System.Type [mscorlib]System.Type::
→GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_0026: call class [System.Core]System.Linq.Expressions.
→ConstantExpression [System.Core]System.Linq.Expressions.
→Expression::Constant(object,
class [mscorlib]System.Type)
IL_002b: call class [System.Core]System.Linq.Expressions.BinaryExpression
→[System.Core]System.Linq.Expressions.Expression::Modulo
→(class [System.Core]System.Linq.Expressions.Expression,
class [System.Core]System.Linq.Expressions.Expression)
IL_0030: ldc.i4.0
IL_0031: box [mscorlib]System.Int32
IL_0036: ldtoken [mscorlib]System.Int32
IL_003b: call class [mscorlib]System.Type
→[mscorlib]System.Type::GetTypeFromHandle(valuetype
→[mscorlib]System.RuntimeTypeHandle)
IL_0040: call class [System.Core]System.Linq.Expressions.
→ConstantExpression [System.Core]System.Linq.Expressions.
→Expression::Constant(object,
class [mscorlib]System.Type)
IL_0045: call class [System.Core]System.Linq.Expressions.
→BinaryExpression [System.Core]System.Linq.Expressions.Expression::
→Equal(class [System.Core]System.Linq.Expressions.Expression,
class [System.Core]System.Linq.Expressions.Expression)
IL_004a: ldc.i4.1
IL_004b: newarr [System.Core]System.Linq.Expressions.ParameterExpression
IL_0050: stloc.1
IL_0051: ldloc.1
IL_0052: ldc.i4.0
IL_0053: ldloc.0
IL_0054: stelem.ref
IL_0055: ldloc.1
IL_0056: call class [System.Core]System.Linq.Expressions.
→Expression`1<!!0> [System.Core]System.Linq.Expressions.Expression::
→Lambda<class [System.Core]System.Func`2<int32,bool>>
→(class [System.Core]System.Linq.Expressions.Expression,
class [System.Core]System.Linq.Expressions.ParameterExpression[])
IL_005b: pop
IL_005c: ret
} // end of method Program::Test2
The expression tree is an in-memory representation of the Lambda Expression. The expression can be compiled and its underlying Lambda Expression can be invoked just like any other Lambda Expression, but, more important, Lambda Expressions can be passed around and transformed by application programming interfaces (APIs) in new ways—for example, LINQ to SQL. The code in Listing 5.20 shows the code used to generate the MSIL in Listings 5.18 and 5.19 and Test2
includes code to explore the expression tree generated.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
namespace EmitLambda
{
class Program
{
static void Main(string[] args)
{
Test();
Test2();
}
static void Test2()
{
Expression<Func<int, bool>> exp =
num ⇒ num % 2 == 0;
// The MSIL is generated from the statement above
Console.WriteLine(“Body: {0}”, exp.Body.ToString());
Console.WriteLine(“Node Type: {0}”, exp.NodeType);
Console.WriteLine(“Type: {0}”, exp.Type);
Func<int, bool> lambda = exp.Compile();
Console.WriteLine(lambda(2));
Console.ReadLine();
}
static void Test()
{
Func<int, bool> lambda = num ⇒ num % 2 == 0;
}
}
}
The code in Test2
after the Expression
definition demonstrates how you can explore the method body, the kind of expression, and argument types. The output from Test2
is shown in Figure 5.3.
When you declare a variable local to a function, that variable lives in the stack memory space of the declaring scope. So, for example, when the function returns, the local variables are cleaned up with the stack memory for that function. All of this means that if you use a local variable in a Lambda Expression, that local variable would be undefined when the function’s stack is cleaned up. As a precaution, in the event a Lambda Expression is to be returned from a function, and that Lambda Expression depends on a local variable, the compiler creates a Closure
(or wrapper class).
A closure is a generated class that includes a field for each local variable used and the Lambda Expression. The value of the local variable(s) is copied to the generated fields to effectively extend the lifetime of the local variables.
If you are familiar with the behavior of stack memory, this explanation makes sense to you. If unfamiliar, don’t worry. The compiler takes care of creating the closure and all of the plumbing is transparent. Listing 5.21 shows a Lambda Expression that uses a local variable and Figure 5.4 shows the MSIL and the generated closure.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LambdaClosure
{
class Program
{
static void Main(string[] args)
{
UsesClosure();
}
static void UsesClosure()
{
string toFind = “ed”;
var words = new string[]{
“ended”, “friend”, “closed”, “potato”};
var matches = words.Select(s ⇒ s.Contains(toFind));
foreach(var str in matches)
Console.WriteLine(str);
Console.ReadLine();
}
}
}
The most fun I had describing currying was to a group at the Ann Arbor Day of .NET at Washtenaw Community College in Michigan: “Currying was invented in the 1930s by the mathematician Carlos Santana.” Wikipedia can tell you about the science and math of currying and who is attributed with its invention. (You are encouraged to read the underscoring explanation of currying.) Here, you’ll get a less-scientific explanation.
Currying is a use of Lambda Expressions in which a multistep expression is broken into several Lambda Expressions. Each Lambda Expression is a feeder to the next expression until the last expression provides the solution. This is sort of like a Turing machine. (“Turing Machines” were described by Alan Turing in the 1940s. Turing machines are basically simple computers that can solve any problem theoretically one step at a time, one piece at a time.) Refer to Listing 5.22 for an example.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LambdaCurry
{
class Program
{
static void Main(string[] args)
{
Func<int, int, int> longLambda = (x,y) ⇒ x + y;
Console.WriteLine(longLambda(3,4));
Console.ReadLine();
// currying
Func<int, Func<int,int>> curry1 = x ⇒ y ⇒ x + y;
Console.WriteLine(curry1(3)(4));
Console.ReadLine();
}
}
}
In the first half, longLambda
accepts two arguments and returns the sum. After the comment // currying, the same behavior is represented by a Lambda Expression that employs currying. The Lambda Expression curry1
accepts a single int
argument and returns a second Lambda y ⇒ x + y
. The result is that you can call curry1
with the first operand. This first call returns the second Lambda Expression, which you can immediately invoke—using the function call notation (arg) to invoke that Lambda Expression.
Lambda Expressions that employ currying are a little hard to write, and it is even harder to figure out what to do with currying Lambdas. It might be useful to employ currying Lambdas with code generators by chunking up long problems into singular Lambda Expressions that are daisy-chained together. It will be interesting to see what the programming community does with currying.
The important thing to remember is that for each step in a multiargument Lambda Expression, simply chain another arg ⇒ expression
in the sequence; when invoking the curried expression, chain the function call operator with the input arguments (as demonstrated in Listing 5.22).
Lambda Expressions are fundamentally shorthand notation for anonymous delegates. They are inline functions. Lambda Expressions return instances of anonymous delegates, such as Func<T>
, Predicate<T>
, and Action<T>
. The delegates are useful in extension methods that use functions to perform operations on arrays and other enumerable types.
Generic delegates, extension methods, and Lambda Expressions are an evolutionary step to LINQ. LINQ is a more natural like query language for .NET. LINQ queries use elements that look similar to query languages like SQL and these LINQ keywords are mapped to extension methods. The arguments to LINQ keywords are mapped to inputs to these extension methods, and the inputs are converted to the required type, usually a generic delegate.
This chapter also included discussions on projections, currying, and closures. Hopefully, these concepts will help you explore Lambda Expressions with your peers and find new and creative ways to use them.
18.118.12.157