Lambda Expressions

Intertech Tutorials

Understanding the Role of Lambda Expressions

C# and VB now support the construction of ‘lambda expressions’. Simply put, lambda expressions are used to simplify working with delegate types. By and large, any delegate object can be substituted with a proper lambda expression (VB does have more restrictions, which are examined later). The compiler will infer the appropriate delegate at compile time.

For example, if a method requires a new delegate object as a parameter, you can pass a lambda expression instead. The end result is typically a cleaner, more functional, code base. Lambdas can reduce the amount of code you need to author by hand. To this end, lambdas are always entirely optional and nothing more than syntactic sugar.

C# lambdas are created using the new lambda operator (=>). VB lambdas are defined by making use of the Function statement.

Although C# and VB lambdas are both used to simplify interaction with delegate types, C# and VB handle them quite differently. As of .NET 3.5, C# lambdas are a tad more flexible than VB lambdas. However, for most situations, either language will allow you to build useful lambda expressions.

A First Look at Lambda Expressions

To understand the usefulness of lambda expressions, consider the .NET generic Predicate<T> delegate type. This delegate can ‘point to’ any method that returns a Boolean and takes a single type parameter as input. This particular delegate can be useful when you need to determine the truth or falsity of a variable based on a mathematical statement.

The List<T>.FindAll() method requires an instance of Predicate<T> as a parameter. Using the Predicate<T> delegate and the List<T>.FindAll() method, you could build a program that finds all even numbers in a list of integers. Here is the program in question, using traditional C# delegate syntax. The output will display the numbers 20, 4, 8 and 44.


// C#
class Program
{
static void Main(string[] args)
{
// Fill a list using C# 3.0 collection init syntax.
List<int> list = new List<int>() {20, 1, 4, 8, 9, 44};

// Create a Predicate<T> object for use by the List<T>.FindAll()
// method.
Predicate<int> callback = new Predicate<int>(IsNumberEven);

// Call FindAll(), passing the delegate object.
List<int> evenNumbers = list.FindAll(callback);

// Print out the result set.
Console.WriteLine("Here are the even numbers:");
foreach (int evenNumber in evenNumbers)
{
 Console.WriteLine(evenNumber);
}
}

// Target for the Predicate<T> delegate.
static bool IsNumberEven(int i)
{
// Is it an even number?
return (i % 2) == 0;
}
}

// Target for the Predicate<T> delegate.
static bool IsNumberEven(int i)
{
// Is it an even number?
return (i % 2) == 0;
}
}

The VB code is very similar. Recall, however, that VB does not allow collection initialization syntax. Thus, List(Of T) is filled via a call to AddRange().


' VB
Option Strict On

Module Program
Sub Main()
Dim list = New List(Of Integer)()
list.AddRange(New Integer() {20, 1, 4, 8, 9, 44})

' Create a Predicate<T> object for use by the List<T>.FindAll()
' method.
Dim callback As New Predicate(Of Integer)(AddressOf IsNumberEven)

' Call FindAll(), passing the delegate object.
Dim evenNumbers As List(Of Integer) = list.FindAll(callback)

' Print out the result set.
Console.WriteLine("Here are the even numbers:")
For Each evenNumber As Integer In evenNumbers
Console.WriteLine(evenNumber)
Next
End Sub

' Target for the Predicate(Of T) delegate.
Function IsNumberEven(ByVal i As Integer) As Boolean
' Is it an even number?
Return (i Mod 2) = 0
End Function
End Module

One problem with traditional delegates is that they typically force you to build target methods (such as the IsNumberEven() function), which are usually only called by the delegate itself. Under C# 2.0, anonymous methods provide a somewhat cleaner alternative, given that you can ‘inline’ the delegate target. VB did not, and does not, support anonymous methods (although use of the Function statement is similar).

Thus, the previous C# code could be remodeled to use an anonymous method as follows. Notice that you no longer need to have a separate target method for the Predicate<T> delegate. Also recall that C# anonymous methods do not require specifying the type of the underlying delegate. You can simply use the delegate keyword. The compiler will infer the correct underlying delegate (Predicate<T>).


static void Main(string[] args)
{
List<int> list = new List<int>() {20, 1, 4, 8, 9, 44};

// Use an C# 2.0 anonymous method.
List<int> evenNumbers = list.FindAll(delegate(int i)
{ return (i % 2) == 0; } );

Console.WriteLine("Here are the even numbers:");
foreach (int evenNumber in evenNumbers)
{
Console.WriteLine(evenNumber);
}
}

Although this C# example can be considered ‘better’, there are still some problems:

Now consider the same code retrofitted to use the C# lambda operator (=>). In the code below, notice how there is no trace whatsoever of the delegate type. Under the hood, the C# compiler generates an appropriate anonymous method. Recall that C# anonymous methods are just delegates in disguise.


static void Main(string[] args)
{
List<int> list = new List<int>() {20, 1, 4, 8, 9, 44};

// Use a C# 3.0 lambda expression.
// Remember that this is just an anonymous method in disguise!
List<int> evenNumbers = list.FindAll(i => (i % 2) == 0);

Console.WriteLine("Here are the even numbers:");
foreach (int evenNumber in evenNumbers)
{
Console.WriteLine(evenNumber);
}
}

VB programmers can author a very similar code base using the Function statement. Again, notice how you have no need to author a separate target method for the Predicate(Of T) delegate. Furthermore, you do not need to make use of the delegate type whatsoever, similar to a C# anonymous method. The VB compiler will infer this.


' VB
Option Strict On

Module Program
Sub Main()
Dim list = New List(Of Integer)()
list.AddRange(New Integer() {20, 1, 4, 8, 9, 44})

' Call FindAll() using a VB lambda.
Dim evenNumbers As List(Of Integer) = _
list.FindAll(Function(i) (i Mod 2) = 0)

' Print out the result set.
Console.WriteLine("Here are the even numbers:")
For Each evenNumber As Integer In evenNumbers
Console.WriteLine(evenNumber)
Next
End Sub
End Module

Copyright (c) 2008-2013. Intertech, Inc. All Rights Reserved. This information is to be used exclusively as an online learning aid. Any attempts to copy, reproduce, or use for training is strictly prohibited.