Lambda Expressions, Func and Action Delegates

Func and Action Delegates take the C# language to a new functional programming level. Lambda Expressions are tightly entwined in the LINQ infrastructure. We take a brief look at each.

Lambda Expressions

A lambda expression is an unnamed method that you write in place of a delegate instance. The compiler immediately converts the lambda expression to either:

• A delegate instance.
• An expression tree, of type Expression<TDelegate>, representing the code inside the lambda expression in a traversable object model.
This allows the lambda expression to be interpreted later at runtime.  

Given the following delegate type:
delegate int Squarer (int i);
we could assign and invoke the lambda expression x => x * x as follows:
Squarer sqr = x => x * x;
Console
.WriteLine (sqr(3));    

Lambda expressions are simply definitions of functions, but in a very concise form with little syntactical ceremony. Because the underlying platform supports delegates as a way to pass pieces of code around, lambda expressions can leverage this infrastructure and be converted into delegates. The following example illustrates the creation of a function that adds two numbers together, with and without the use of lambdas:

// C# 2.0  
Func<int, int, int> add20 = delegate (int a, int b) { return a + b; };

// C# 3.0  
Func<int, int, int> add30 = (a, b) => a + b;


A lambda expression has the following form:
(parameters) => expression-or-statement-block

You can omit the parentheses if there is only one parameter of an inferable type. In the example, there is a single parameter, x, and the expression is x * x:
x => x * x;

Each parameter of the lambda expression corresponds to a delegate parameter, and the type of the expression (which may be void) corresponds to the return type of the delegate.

In the example, x corresponds to parameter i, and the expression x * x corresponds to the return type int, therefore being compatible with the Squarer delegate:
delegate int Squarer (int i);

A lambda expression’s code can be a statement block instead of an expression. We can rewrite the example as follows:
x => { return x * x; };

Lambda expressions are used most commonly with the Func and Action delegates:
Func<int,int> sqr = x => x * x;

An example of an expression that accepts two parameters and returns an int:
Func<string,string,int> totalLength = (s1, s2) => s1.Length + s2.Length;
int total = totalLength ("howdy", "there");   // output is 10;

Explicitly Specifying Lambda Parameter Types

The compiler can usually infer the type of lambda parameters contextually. When this is not the case, you must specify the type of each parameter explicitly.
In the following expression:
Func<int,int> sqr = x => x * x;
The compiler uses type inference to infer that x is an int.
We could explicitly specify x’s type as follows:
Func<int,int> sqr = (int x) => x * x;

Capturing Outer Variables

A lambda expression can reference the local variables and parameters of the method
in which it’s defined (outer variables). For example:
static void Main()
{
  int factor = 6;
  Func<int, int> multiplier = n => n * factor;
Console.WriteLine (multiplier (3));           // 18
}

Outer variables referenced by a lambda expression are called captured variables. A lambda expression that captures variables is called a closure. Captured variables are evaluated when the delegate is actually  invoked, not when the variables were captured:
int factor = 2;
Func<int, int> multiplier = n => n * factor;
factor = 10;
Console
.WriteLine (multiplier (3));           // 30

Lambda expressions can themselves update captured variables:
int seed = 0;
Func<int> item = () => seed++;
Console
.WriteLine (item());           // 0
Console
.WriteLine (item());           // 1
Console
.WriteLine (seed);                // 2

Captured variables have their lifetimes extended to that of the delegate. In the following example, the local variable seed would ordinarily disappear from scope when Item finished executing. But because seed has been captured, its lifetime is extended to that of the capturing delegate, item:
static Func<int> Item()
{
  int seed = 0;
  return () => seed++;      // Returns a closure
}
static void Main()
{
  Func<int> item = Item();
Console.WriteLine (item());      // 0
Console.WriteLine (item());      // 1
}


Action Delegates

You can use the Action<T> delegate to pass a method as a parameter without explicitly declaring a custom delegate. The encapsulated method must correspond to the method signature that is defined by this delegate. This means that the encapsulated method must have one parameter that is passed to it by value, and it must not return a value. Typically, such a method is used to perform an operation.

When you use the Action<T> delegate, you do not have to explicitly define a delegate that encapsulates a method with a single parameter.   The following example  instantiates the Action<T> delegate instead of explicitly defining a new delegate and assigning a named method to it.

using System;
using System.Windows.Forms;

public class TestAction1
{
   public static void Main()
   {
      Action<string> messageTarget;

      if (Environment.GetCommandLineArgs().Length > 1)
         messageTarget = ShowWindowsMessage;
      else
         messageTarget = Console.WriteLine;

      messageTarget("Hello, World!");  
   }      

   private static void ShowWindowsMessage(string message)
   {
      MessageBox.Show(message);      
   }
}


You can also use the Action<T> delegate with anonymous methods in C#:

using System;
using System.Windows.Forms;

public class TestAnonMethod
{
   public static void Main()
   {
      Action<string> messageTarget;

      if (Environment.GetCommandLineArgs().Length > 1)
         messageTarget = delegate(string s) { ShowWindowsMessage(s); };
       else
         messageTarget = delegate(string s) { Console.WriteLine(s); };

      messageTarget("Hello, World!");
   }

   private static void ShowWindowsMessage(string message)
   {
      MessageBox.Show(message);      
   }
}

You can also assign a lambda expression to an Action<T> delegate instance, as the following example illustrates.


using System;
using System.Windows.Forms;

public class TestLambdaExpression
{
   public static void Main()
   {
      Action<string> messageTarget;

      if (Environment.GetCommandLineArgs().Length > 1)
         messageTarget = s => ShowWindowsMessage(s);
       else
         messageTarget = s => Console.WriteLine(s);

      messageTarget("Hello, World!");
   }

   private static void ShowWindowsMessage(string message)
   {
      MessageBox.Show(message);      
   }
}

Func<T, TResult> Delegate

Encapsulates a method that has one parameter and returns a value of the type specified by the TResult parameter.

You can use this delegate to represent a method that can be passed as a parameter without explicitly declaring a custom delegate. The encapsulated method must correspond to the method signature that is defined by this delegate. This means that the encapsulated method must have one parameter that is passed to it by value, and that it must return a value.

The following code explicitly declares a delegate named ConvertMethod and assigns a reference to the UppercaseString method to its delegate instance

using System;

delegate string ConvertMethod(string inString);

public class DelegateExample
{
   public static void Main()
   {
      // Instantiate delegate to reference UppercaseString method
      ConvertMethod convertMeth = UppercaseString;
      string name = "Dakota";
      // Use delegate instance to call UppercaseString method
     Console.WriteLine(convertMeth(name));
   }

   private static string UppercaseString(string inputString)
   {
       return inputString.ToUpper();
   }
}

The following example simplifies this code by instantiating the Func<T, TResult> delegate instead of explicitly defining a new delegate and assigning a named method to it.

using System;

public class GenericFunc
{
   public static void Main()
   {
      // Instantiate delegate to reference UppercaseString method
      Func<string, string> convertMethod = UppercaseString;
      string name = "Georgia";
      // Use delegate instance to call UppercaseString method
     Console.WriteLine(convertMethod(name));
   }

   private static string UppercaseString(string inputString)
   {
       return inputString.ToUpper();
   }
}


You can also use the Func<T, TResult> delegate with anonymous methods in C#, as the following example illustrates.

using System;

public class Anonymous
{
   public static void Main()
   {
      Func<string, string> convert = delegate(string s)
         { return s.ToUpper();};

      string name = "Dakota";
     Console.WriteLine(convert(name));  
   }
}


You can also assign a lambda expression to a Func<T, TResult> delegate, as the following example illustrates

using System;

public class LambdaExpression
{
   public static void Main()
   {
      Func<string, string> convert = s => s.ToUpper();

      string name = "Perry";
     Console.WriteLine(convert(name));  
   }
}

The underlying type of a lambda expression is one of the generic Func delegates. This makes it possible to pass a lambda expression as a parameter without explicitly assigning it to a delegate. In particular, because many methods of types in the System.Linq namespace have Func<T, TResult> parameters, you can pass these methods a lambda expression without explicitly instantiating a Func<T, TResult> delegate.

The following example demonstrates how to declare and use a Func<T, TResult> delegate. This example declares a Func<T, TResult> variable and assigns it a lambda expression that converts the characters in a string to uppercase. The delegate that encapsulates this method is subsequently passed to the Enumerable.Select method to change the strings in an array of strings to uppercase.


using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

static class FuncDemo
{
   static void Main(string[] args)
   {
      // Declare a Func variable and assign a lambda expression to it
      // The method accepts a string and converts it to uppercase.
      Func<string, string> selector = str => str.ToUpper();

      // Create an array of strings.
      string[] words = { "one", "two", "three", "four" };
      // Query the array and select strings.
      IEnumerable<String> aWords = words.Select(selector);

      // Output the results to the console.
      foreach (String word in aWords)
         Console.WriteLine(word);
   }
}      
/*
This code example produces the following output:

   ONE
   TWO
   THREE
   FOUR
*/

There is much more to Lambdas, Func and Action delegates. This short article touches on the main points of interest.

By Peter Bromberg   Popularity  (5466 Views)