Page 1 of 1

Lambdas (the => operator). What are they, and how are they used? A brief description of anonymous methods and lambdas.

#1 Curtis Rutland  Icon User is offline

  • (╯□)╯︵ (~ .o.)~
  • member icon


Reputation: 4437
  • View blog
  • Posts: 7,719
  • Joined: 08-June 10

Posted 09 June 2010 - 01:11 PM

*
POPULAR

The MSDN defines a lambda expression as the following:

Quote

A lambda expression is an anonymous function that can contain expressions and statements, and can be used to create delegates or expression tree types.


Ok, so what does that mean? Here's a good read on the evolution of anonymous methods in C#.

Anyway, an anonymous method is exactly what it sounds like. It's a method that is not named. They can be quite useful in certain situations, especially when you need to provide a method that can be quite short and doesn't need to be re-usable.

So, a common task: show a message box when you click a button. Old code that most of us know how to write:
public Form1()
{
    InitializeComponent();
    Button b1 = new Button() { Text = "Button" };
    this.Controls.Add(b1);
    b1.Click += new EventHandler(b1_Click);
}

void b1_Click(object sender, EventArgs e)
{
    throw new NotImplementedException();
}


Easy enough, but why do I need to write a named method for one button? It takes up space and it doesn't need to be re-used for anything else.

So, a "better" (in this case, shorter) way to do it might be this:
public Form1()
{
    InitializeComponent();
    Button b1 = new Button() { Text = "Button" };
    this.Controls.Add(b1);
    b1.Click += delegate(object sender, EventArgs e) { MessageBox.Show("Clicked"); };
}


But there's even shorter syntax:
public Form1()
{
    InitializeComponent();
    Button b1 = new Button() { Text = "Button" };
    this.Controls.Add(b1);
    b1.Click += (sender, e) => MessageBox.Show("Clicked");
}


To break the actual syntax of a lambda down:
(arg1, arg2, ...argN) => { statement1; statement2; ... return statementN; };

The parenthetical section contains the parameter names. You don't provide a type for these parameters; they are automatically typed by the signature of the expected delegate. Note that for a single parameter, the parentheses are optional, and in fact often omitted. For zero or more than one, they are required.

The " => " is actually an operator. You can read it as "goes to."

The {bracketed} section is the method body. It contains standard statements. They may or may not (but often do) make use of the parameters. The previous example did not, the following ones in the article will. Note that for single statements, the brackets are optional and often omitted, as in this example. For multiple statements, they are required. This is similar to ifs and loops.

The return type of the lambda is automatic, just like the parameter types. It is defined by the signature it is supposed to match. In the previous example, its return type is void. In the following example, we will use a lambda that returns a boolean value. If you do use a single statement, and the delegate is not of return type void, the result of the single statement will be returned. If you do use multiple statements and brackets, you must specifically return a value using the return keyword.

So, perhaps a (slightly) more practical example. We have an unsorted list of integers that we need to filter to all values greater than 4. Luckily enough, the System.Linq library includes extension methods for IEnumerables, one of which happens to be Where(). Where() expects a Function as a parameter. Not a function call, but a reference to a function itself (a delegate). Where() will, behind the scenes, loop through each element of the calling IEnumerable, pass each value to the provided function reference, and for each value that returns true, will store it in a new IEnumerable, which it returns when it reaches the end of the caller.

public Form1()
{
    InitializeComponent();
    int[] list = new int[] { 1, 3, 2, 7, 4, 9, 6, 8, 5 };
    int[] result1 = list.Where(IsGreaterThanFour).ToArray();
    int[] result2 = list.Where(delegate(int x) { return x > 4; }).ToArray();
    int[] result3 = list.Where(x => x > 4).ToArray();
}

private bool IsGreaterThanFour(int x)
{
    return x > 4;
}


Again, we see three ways to do the same thing. Each way passes a method reference to the Where method. The first uses a named method, a solution that is pretty unappealing, considering the brevity and lack of need-to-reuse of the code in question. The second uses an anonymous method, and the third uses the lambda shortcut syntax.

Just for fun
In fact, there is a fourth way using the same method, in LINQ:
int[] result4 = (from x in list
                 where x > 4
                 select x
                ).ToArray();

This syntax seems overkill for this example, and it is. But it can be useful for larger queries against object data sources, because of its similarity to SQL and its ease of visual parsing.

You will find the most use for lambdas in your Linq extension methods for IEnumerables, because most of them expect a function reference. Once you understand how the syntax works, these become much easier to write.

Another quick example:
We have a list of strings, and a custom class (MyClass) that holds a string (Value) and an int (Count). We want to create a new List<MyClass> from our List<string>.

public class MyClass
{
    public string Value { get; set; }
    public int Count { get; set; }
}

public Form1()
{
    InitializeComponent();
    List<string> list = new List<string>() { "first", "second", "a third string" };
    List<MyClass> result = list.Select(x => new MyClass { Value = x, Count = x.Length }).ToList();
}


This lambda takes a string as a parameter, and returns a MyClass. Select() will loop through the calling IEnumerable, and store each returned value in another IEnumerable which it returns when it's done. The lambda we wrote creates a new MyClass from a string and returns the value it creates, which is stored in an IEnumerable, and returned at the end of the Select.

That's all for today, hope this has been enlightening. If you have any questions, let me know.

Is This A Good Question/Topic? 10
  • +

Replies To: Lambdas (the => operator). What are they, and how are they used?

#2 RandomlyKnighted  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 115
  • View blog
  • Posts: 1,356
  • Joined: 14-January 10

Posted 08 March 2013 - 05:15 PM

I'm looking at your code where you listed 3 different ways of getting an array of values greater than 4 from the original array. In result2 you are declaring x an integer. However, in result 3 you did not declare x. Do you not have to declare the passing parameter when using lambda expressions?

This post has been edited by RandomlyKnighted: 08 March 2013 - 05:15 PM

Was This Post Helpful? 0
  • +
  • -

#3 Curtis Rutland  Icon User is offline

  • (╯□)╯︵ (~ .o.)~
  • member icon


Reputation: 4437
  • View blog
  • Posts: 7,719
  • Joined: 08-June 10

Posted 08 March 2013 - 06:08 PM

Are you referring to this code?

int[] result2 = list.Where(delegate(int x) { return x > 4; }).ToArray();
int[] result3 = list.Where(x => x > 4).ToArray();


The types on parameters for lambdas can be inferred by the compiler. It's actually quite the chain of inferences here.

The Where extension method is defined as:

IEnumerable<T> Where<T>(this IEnumerable<T> source, Func<T, Bool> predicate)


To explain that in english, the Where method returns an IEnumerable of type T. It takes a parameter of a Collection of T. (Because this is an extension method, the first parameter is not passed in parenthesis, it's actually the collection it's called from. In our example, list is the first parameter). The second parameter is a Function. This Function parameter takes a parameter of T, the same type as the collection, and returns a boolean. This function can be defined as a named function, a delegate, or a lambda.

So, because the Where method is called on a List<int>, which is an IEnumerable<int>, all the other <T>s become <int>s. It returns a colleciton of ints, and the lambda's parameter is an int.

To sum that all up: the lambda's parameter doesn't have to be explicitly typed because the type is implied by context; the compiler automatically knows what type of parameter we're using.

If you wanted to explicitly declare the type, you could:

int[] result3 = list.Where((int x) => x > 4).ToArray();

Was This Post Helpful? 0
  • +
  • -

#4 AdamSpeight2008  Icon User is offline

  • MrCupOfT
  • member icon


Reputation: 2241
  • View blog
  • Posts: 9,416
  • Joined: 29-May 08

Posted 08 March 2013 - 06:37 PM

If it helps you, something very similar to the following code is implemented in extension methods.

.Where (aka Filter )
IEnumerable<T> Where<T>(this IEnumerable<T> source, Func<T, Bool> predicate)
{
  if( source == null ) return Enumerable.Empty<T>();
  foreach( elem in source )
  { if( predicate( elem ) )
    {
      yield return elem;
    }
  }
}



.Select (aka Transform or Map )
IEnumerable<U> Select<T,U>(this IEnumerable<T> source, Func<T, U> selector)
{
  if( source == null ) return Enumerable.Empty<T>();
  foreach( elem in source )
  { 
      yield return selector( elem );
  }
}



.Aggregate (aka Reduce)
IEnumerable<U> Aggregate<T,U>(this IEnumerable<T> source,
 inital : U,  Func<U, T, U> aggregateFN )
{
  if( source == null ) return inital;
  U value = initial;
  foreach( elem in source )
  { 
      value = aggregateFN( value, elem );
  }
  return value;
}



.SelectMany (aka Bind)
IEnumerable<U> SelectMany<T,R>(this IEnumerable<T> source,
 selector : Func<T,IEnumerable<R>>)
{
  if( source == null ) return inital;
  foreach( elem in source)
  {
    foreach( x in selector( elem ) )
    {
      yield return x;
    } 
  }
}

This post has been edited by AdamSpeight2008: 08 March 2013 - 06:45 PM

Was This Post Helpful? 1
  • +
  • -

#5 RandomlyKnighted  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 115
  • View blog
  • Posts: 1,356
  • Joined: 14-January 10

Posted 08 March 2013 - 06:43 PM

Thanks for the help. I'm realizing that there are some gaps between what I know and what I need to know in order to have a good understanding of Lambda expressions.
Was This Post Helpful? 0
  • +
  • -

#6 AdamSpeight2008  Icon User is offline

  • MrCupOfT
  • member icon


Reputation: 2241
  • View blog
  • Posts: 9,416
  • Joined: 29-May 08

Posted 08 March 2013 - 07:21 PM

If you understand what the Func<T,U> means, it'll help.

Let's begin with the last generic type parameter U, as this always the return type of the function, what it returns. So this means the func will at least have one generic type parameter. T and U can be the same type
The other generic types are the input parameter types the function requires.

For example
string GetTextCode( string p1, int p2)
{
}


is a Func<string, int, string>. Since it takes in a string and integer, then
return (as its output) a string.

int GetRandomNumber()
{
  return 4;
}



is a Func<int> since it takes no input parameters and return an integer.

So you can see the a Func<T> parameter can accept as input, any function that matches the type signature (parameter type and return types).

And lambda function is just a neat way of specifying the function, inline.

Now on to Action<T> this you think as Func<T,void>.
It is a method that has not return type, eg it doesn't return anything.
Hence the name action, since in just performs an action.
Spoiler


Lambdas do have a subtleties and gotchas you have to be aware of though.
They build expression trees behind the scenes the represent the function code, so it can operate lazily.
Somethings get lifted into a closure.
MSDN Article on Lambda Expression

This post has been edited by AdamSpeight2008: 08 March 2013 - 07:53 PM

Was This Post Helpful? 1
  • +
  • -

#7 RandomlyKnighted  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 115
  • View blog
  • Posts: 1,356
  • Joined: 14-January 10

Posted 08 March 2013 - 07:35 PM

Thanks for that clarification! That makes a lot more sense. So how do you know when you should use a lambda expression?
Was This Post Helpful? 0
  • +
  • -

#8 AdamSpeight2008  Icon User is offline

  • MrCupOfT
  • member icon


Reputation: 2241
  • View blog
  • Posts: 9,416
  • Joined: 29-May 08

Posted 08 March 2013 - 07:49 PM

If I take the example from the original post.
int[] result4 = (from x in list
                 where x > 4
                 select x
                ).ToArray();


and transform it into something like what the compiler produces.
// generated by the compiler 
// have names a coder can't generate.
Bool _anonymous1 ( int x)
{
  return x > 4
}
int _anonymous2 (int x)
{
 return x
}
int[] result4 = 
Enumerable.ToArray(Enumerable.Select( Enumerable.Where( list.AsEnumerable(), _anonymous1 ) , _anonymous2 ));



You'll actual see that thexs are actually represent different xs.

Quote

So how do you know when you should use a lambda expression?

It up to you (as the developer) to decide mostly.
They are really useful and are becoming more prevalent in the framework.
eg Tasks and Async callbacks.

This post has been edited by AdamSpeight2008: 08 March 2013 - 07:55 PM

Was This Post Helpful? 0
  • +
  • -

Page 1 of 1