Generic Delegates and Lambda Expressions

Introduction

Archimedes said: “Give me a place to stand and a lever long enough and I will move the world.” Archimedes was referring to the potential power of tools to magnify the amount of work that can be done. Generic delegates and Lambda Expressions are tools. These tools magnify the amount of work you can do with less code.

In this article, you will explore generic delegates, the ForEach(Of T) generic method, and Lambda Expressions for VB9. Besides the ForEach(Of T) method, you’ll look at the Action, Func, and Predicate anonymous delegates.

A Quick Review of Lambda Expressions

When you hear someone talk about functional programming, they are referring to Lambda Expressions. Lambda Expressions have been in other languages for a while and they are based on 70 year old mathematics, Lambda Expressions.

For your purposes, Lambda Expressions are basically inline functions that are used where historically you would have used delegates. You use Lambda Expressions instead because of their concise nature they fit in more naturally with LINQ and other new .NET technologies. A Lambda Expression is comprised of the keyword Function, a parameter list, and is immediately followed by the statement or expression that makes up the body of the function.

In VB9, Lambda Expressions begin with Function and have n-number of arguments. If the types can be inferred, the argument need not be provided explicitly. If the type cannot be inferred or Option Strict On is set, the type (As Type) must be provided for the arguments. Immediately following the Function(arg1, arg2, …, argn) part of the expression, you need to supply the expression. The expression must return a result. You’ll see some Lambda Expressions in the code samples.

The generic delegate Action(Of T) is a rough implementation of the Command behavior pattern. To use Action(Of T), declare a variable of type Action(Of T) and specify the type for T; for example, Integer. Then, initialize the Action object with the address of a delegate that accepts the number and type of arguments expressed in the Action declaration. (You can provide more than one argument for Action.) Listing 1 demonstrates how to use Action with a single argument.

Listing 1: Declaring and initializing an instance of Action(Of T) where T is expressed as an Integer and it is assigned to the address of a subroutine, Print.

Sub DemoActionOfT()
   ' Can't use Lambda here (can in C#) because VB Lambdas
   ' have a return value
   Dim Action As Action(Of String) = AddressOf Print
   Action("Hello World")
   Console.ReadLine()
End Sub

Sub Print(ByVal str As String)
   Console.WriteLine(str)
End Sub

In VB9, you can’t initialize an Action object with a Lambda Expression because Lambda Expressions in VB9 are designed around expressions—code that has a return value—not statements—or, lines of code that yield no return value.

Easy Iteration with Array.ForEach(Of T)

ForEach(Of T) performs a specified action over an array of items where T indicates the type of the item. The first argument to ForEach is the array and the second arguments is the delegate of an instance of Action(Of T). Listing 2 demonstrates how to use ForEach. The array is represented by the anonymous type declaration of numbers and the Action(Of T) object is represented by the AddressOf Print.

Listing 2: Demonstrates the ForEach generic method that iterates over an array of items and performs the operation expressed by the Action delegate.

Sub DemoForEach()
   Dim numbers() = {1, 2, 3, 4, 5}

   Array.ForEach(numbers, AddressOf Print)
   Console.ReadLine()

End Sub

Sub Print(ByVal str As String)
   Console.WriteLine(str)
End Sub

Capturing Behaviors with Func(Of T)

The generic delegate Func(Of T) is designed to accept n-arguments—parameterized types—and the nth (or last argument) is the return type. For example, Func(Of Integer, Integer, Integer) means that the generic delegate accepts three integers and returns an integer. Because Func has a return argument, it can be initialized with a Lambda Expression. Listing 3 demonstrates a Lambda Expression that is assigned to an instance of Func(Of Integer, Integer, Integer) where the behavior performs simple arithmetic.

Listing 3: A Lambda Expression that accepts to arguments assigned to an instance of Func(Of T, T, T).

Sub DemoFunctionOfT()

   ' Argument 1, 2, n and result, the last argument
   Dim Adder As Func(Of Integer, Integer, Integer) = _
   Function(x, y) x + y

   Console.WriteLine(Adder(4, 5))
   Console.ReadLine()
End Sub

Implementing Comparison Behaviors with Predicate(Of T)

The Predicate delegate is designed to accept arguments and return a Boolean. Its intended use is to accept test parameters and return a Boolean indicating the Boolean result of the test. In the example in Listing 4, the code tests the length of the input string to determine whether it is five characters in length.

Listing 4: The Predicate generic delegate demonstrates how to capture a test with a Boolean result.

Sub DemoPredicateOfT()
   ' Can use Lambda because predicate has return of Boolean
   Dim pred As Predicate(Of String) = _
   Function(str) str.Length = 5

   Console.WriteLine("'Hello' is five characters long is {0}", _
      pred("hello"))
   Console.ReadLine()
End Sub

Although you aren’t required to assign Lambda Expressions to generic delegates, sometimes using the generic delegates make the code clearer and generic delegates are a convenient means of reusing lambda Expressions (or delegates, as is the case with Action) or even passing them around to functions.

Summary

Lambda Expressions in VB9 start with the Function keyword and parameters. The body of the Lambda Expression (in VB9) is an expression, aka code that yields a result. Lambda Expressions can be used directly or assigned to the generic delegates like Predicate and Func. Having these convenient storage types for Lambda Expressions makes it easier to reuse Lambda Expressions and pass them as arguments.

Remember that Lambda Expressions are basically shorthand for delegates. You can use Lambda Expressions anywhere you would use a regular delegate. The key benefit is that Lambda Expressions are more concise and consequently fit nicely with new code added to the .NET framework to support LINQ (Language Integrated Query).

About the Author

Paul Kimmel is the VB Today columnist for www.codeguru.com and has written several books on object-oriented programming and .NET. Check out his upcoming book LINQ Unleashed for C# due in Spring 2008. You may contact him for technology questions at pkimmel@softconcepts.com.

If you are interested in joining or sponsoring a .NET Users Group, check out www.glugnet.org. Glugnet opened a users group branch in Flint, Michigan in August 2007. If you are interested in attending, check out the www.glugnet.org web site for updates.

Copyright © 2007 by Paul T. Kimmel. All Rights Reserved.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read