Friday, December 08, 2006 4:32 AM bart

C# 3.0 Feature Focus - Part 5 - Lambda Expressions

Introduction

In this C# 3.0 Feature Focus Week we'll focus on the new language features that will be part of C# 3.0 "Orcas". As you already might know, the major innovation on Orcas is the LINQ (Language Integrated Query) technology that will make data queries a first class citizen of the language (C# 3.0 and VB 9.0). In order to unlock this potential, a set of language features were added to the language that all work in concert to enable LINQ stuff. However, all of those individual features prove to be useful in isolation as well. Later, we'll focus on LINQ itself.

 

Lambda Expressions

Time for yet another noise eliminating feature in C# 3.0, based on functional programming concepts (lambda calculus). Consider the following basic sample in which we want to have to some calculate function that takes three arguments, a function pointer (read: delegate) to a binary operation function (read: a method with signature int BinOp(int a, int b)) and two operandi, as a generic way to deal with binary operation calculations like addition, subtraction, multiplication, division, modulo, whatever you can think of. Defining this is piece of cake, using a simple delegate:

using System;

class Lambda
{
   delegate int BinOp(int a, int b);

   static void Main()
   {
   }

   static int Calculate(BinOp op, int a, int b)
   {
      return op(a,b);
   }
}

However, using it might be a bit more cumbersome. Here's what it'd look like in C# 1.0 (noise underlined):

using System;

class Lambda
{
   delegate int BinOp(int a, int b);

   static void Main()
   {
      int res = Calculate(Sum, 1, 2);

      Console.WriteLine(res);

   }

   static int Sum(int a, int b)
   {
      return a + b;
   }

   static int Calculate(BinOp op, int a, int b)
   {
      return op(a,b);
   }
}

In C# 2.0, the need for a separate method definition was eliminated using anonymous methods, yielding the following (cleaner?) syntax:

using System;

class Lambda
{
   delegate int BinOp(int a, int b);

   static void Main()
   {
      int res = Calculate(delegate (int a, int b) { return a + b; }, 1, 2);

      Console.WriteLine(res);

   }

   static int Calculate(BinOp op, int a, int b)
   {
      return op(a,b);
   }
}

In the end, we just want to express that two variables with dummy names a and b have to be transformed into a result that's the sum of both numbers, i.e. a+b. Lambda expressions allow us to do just this, as follows:

using System;

class Lambda
{
   delegate int BinOp(int a, int b);

   static void Main()
   {
      int res = Calculate((a,b) => a + b, 1, 2);

      Console.WriteLine(res);

   }

   static int Calculate(BinOp op, int a, int b)
   {
      return op(a,b);
   }
}

Notice we don't need to specify the types of a and b since the compiler can infer the types automatically. Behind the scenes, the compiler transforms the lambda expression in an anonymous method, which on its turn is transformed into a randomly named private method:

.method private hidebysig static int32 '<Main>b__0'(int32 a, int32 b) cil managed
{
   .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
   .locals init (int32 V_0)
   IL_0000: nop
   IL_0001: ldarg.0
   IL_0002: ldarg.1
   IL_0003: add
   IL_0004: stloc.0
   IL_0005: br.s IL_0007
   IL_0007: ldloc.0
   IL_0008: ret
} // end of method Lambda::'<Main>b__0'

and a "cached anonymous delegate":

.field private static class Lambda/BinOp '<>9__CachedAnonymousMethodDelegate1'
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )

Finally, Main creates a BinOp delegate object pointing to the anonymous method (IL_0009, IL_000f) and stores it in the cache anonymous delegate (IL_0014):

.method private hidebysig static void Main() cil managed
{
   .entrypoint
   .locals init (int32 V_0)
   IL_0000: nop
   IL_0001: ldsfld class Lambda/BinOp Lambda::'<>9__CachedAnonymousMethodDelegate1'
   IL_0006: brtrue.s IL_001b
   IL_0008: ldnull
   IL_0009: ldftn int32 Lambda::'<Main>b__0'(int32,int32)
   IL_000f: newobj instance void Lambda/BinOp::.ctor(object,native int)
   IL_0014: stsfld class Lambda/BinOp Lambda::'<>9__CachedAnonymousMethodDelegate1'
   IL_0019: br.s IL_001b
   IL_001b: ldsfld class Lambda/BinOp Lambda::'<>9__CachedAnonymousMethodDelegate1'
   IL_0020: ldc.i4.1
   IL_0021: ldc.i4.2
   IL_0022: call int32 Lambda::Calculate(class Lambda/BinOp,int32,int32)
   IL_0027: stloc.0
   IL_0028: ldloc.0
   IL_0029: call void [mscorlib]System.Console::WriteLine(int32)
   IL_002e: nop
   IL_002f: ret
} // end of method Lambda::Main

 

Quiz

Derive a delegate signature for each of the following lambda expressions and perform an invocation:

  • () => 123
  • () => {}
  • a => 2 * a
  • (a, b) => a * b
  • s => Console.WriteLine("Hello {0}", s);
  • () => { string s = Console.ReadLine(); return s; }

Think about the following code fragment and find out how it works (if it works of course):

using System;

class Lambda
{
   delegate int BinOp(int a, int b);

   static void Main()
   {
      int c = 3;
      int res = Calculate((a,b) => a + b + c, 1, 2);

      Console.WriteLine(res);

   }

   static int Calculate(BinOp op, int a, int b)
   {
      return op(a,b);
   }
}

I'll post about this later on.

kick it on DotNetKicks.com

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Filed under:

Comments

# The IQueryable tales - LINQ to LDAP - Part 0

Thursday, April 05, 2007 7:04 PM by B# .NET Blog

Here we are again for some cool LINQ stuff. In the past I've been blogging on C# 3.0 language innovation

# The IQueryable tales - LINQ to LDAP - Part 1: Key concepts

Friday, April 06, 2007 8:14 AM by B# .NET Blog

Introduction Welcome to the first real part of our LINQ-to-LDAP series. So far, we've been discussing:

# Interesting C# 3.0 Quiz on Lambda Expressions &laquo; The Pursuit of a Life

Pingback from  Interesting C# 3.0 Quiz on Lambda Expressions &laquo; The Pursuit of a Life

# C# 3.0 Feature Focus – Link Collection

Saturday, August 09, 2008 7:13 AM by B# .NET Blog

Collecting a few of my posts for easy quick reference: C# 3.0 Feature Focus - Part 1 - Local Type Inference