DEV Community

loading...
Cover image for WTF Is a Lambda?

WTF Is a Lambda?

jonhilt profile image Jon Hilton Originally published at jonhilton.net ・2 min read

If you're just learning C# you've probably come across something like this and wondered what the weird x=>x is all about...

things.SingleOrDefault(x=>x.Name == "Bob");

To understand what's going on, let's explore a specific example.

Let's say you have a collection of People.

Here's a class to represent a Person...

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Then somewhere in your application, a list of people like this...

private List<Person> People = new List<Person>
{
    new Person { Name = "Bob", Age = 37 },
    new Person { Name = "Brian", Age = 21 },
    new Person { Name = "Amy", Age = 58 }
};

Three people in a list. Each object in the list is an instance of our
Person class.

Now say you want to find the record for "Amy" and display her age.

You could do this manually by looping through each person until you find
the relevant one...

public void Exists()
{
    Person result = null;

    foreach (var person in People)
    {
        if (person.Name == "Amy")
        {
            result = person;
            break;
        }
    }

    if (result != null)
        Console.WriteLine("we found Amy!, she's " + result.Age);
}

Result will be the instance of "Person" for Amy and we can write her age to
the console.

Alternatively, rather than typing out endless loops yourself, you can use
some methods built in to C# lists to save yourself a few keystrokes.

public void Exists()
{
    var amy = People.SingleOrDefault(IsAmy);
}

Under the covers, this will loop through the "People" list, and for each
one it will call IsAmy to determine whether this is the person we want.

What does IsAmy look like I hear you ask.

private bool IsAmy(Person arg)
{
    return arg.Name == "Amy";
}

SingleOrDefault expects a function, which takes a Person and returns a bool.

In here we can put whatever logic we want.

If IsAmy returns true, then we're saying this instance of Person is the
one we want. If we return false we're saying it isn't.

We can "inline" our IsAmy function (to save declaring it separately) like
so...

public void Exists()
{
    var amy = People.SingleOrDefault((Person person) => 
                                        { return person.Name == "Amy" });
}

This is exactly the same as the last example, except instead of declaring a
separate function we're declaring it "inline".

You can see we're still taking in a Person argument, then returning a
true or false (true if "Amy", false if not).

Finally, to make this more concise, we can simplify to this...

public void Exists()
{
    var amy = People.SingleOrDefault(person => person.Name == "Amy");
}

This is another way of declaring the same thing.

Here, we are still taking the Person, applying our condition to it and
returning the result (true or false) but with less keystrokes!

So the left side of the => is the argument (Person), the right side is the
expression which returns a boolean.

Essentially lambdas are really just syntactic sugar and a shorthand way of
declaring a function.

You'll see them used a lot with lists/other collections and therefore in
relation to database queries etc.

Discussion (8)

pic
Editor guide
Collapse
dance2die profile image
Sung M. Kim • Edited

If anyone's coming from "JavaScript" background, you will find that it works very similar to the JavaScript's lambda.

To show the similarity (using the examples in this article)

1 - You can leave out () if you have only one argument passed.

things.SingleOrDefault((x) = >x.Name == "Bob");
// or
things.SingleOrDefault(x => x.Name == "Bob");

2 - You can pass the method name

var amy = People.SingleOrDefault(() => IsAmy());
// or
var amy = People.SingleOrDefault(IsAmy);

Both lambda syntax are the same in C# & JavaScript.

Collapse
elgoorf profile image
Hussein Duvigneau • Edited

You can go one step further:

things.SingleOrDefault(({name}) => name === "Bob");

Collapse
dance2die profile image
Sung M. Kim • Edited

I wasn't able to replicate the object desconstruction in C# as you could do in JavaScript (even after implementing Deconstruct)

dotnetfiddle.net/Wbx3ac

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

// https://dev.to/jonhilt/wtf-is-a-lambda-2dg2

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    // Allow Object Deconstructuring/destructuring
    // https://docs.microsoft.com/en-us/dotnet/csharp/deconstruct#deconstructing-a-user-defined-type-with-discards
    public void Deconstruct(out string name, out int age) {
        name = Name;
        age = Age;
    }

    public override string ToString() {
        return $"Name={this.Name}, Age={this.Age}"; 
    }
}

public class Program
{
    static List<Person> people = new List<Person>
    {
        new Person { Name = "Bob", Age = 37 },
        new Person { Name = "Brian", Age = 21 },
        new Person { Name = "Amy", Age = 58 }
    };

    public static void Main()
    {
        // This works
//      var bob = people.SingleOrDefault(person => person.Name == "Bob");
        // But this doesn't
//      var bob = people.SingleOrDefault(({name}) => name == "Bob");
        var bob = people.SingleOrDefault(((Name, _)) => Name == "Bob");
        Console.WriteLine(bob);
    }
}

And Roslyn 2.0 compiler complains...


}
Compilation error (line 39, col 48): Invalid expression term '=>'
Compilation error (line 39, col 48): Syntax error, ',' expected
Compilation error (line 39, col 51): Syntax error, ',' expected
Compilation error (line 39, col 38): The type or namespace name 'Name' could not be found (are you missing a using directive or an assembly reference?)
Compilation error (line 39, col 44): The type or namespace name '_' could not be found (are you missing a using directive or an assembly reference?)
Compilation error (line 39, col 37): Predefined type 'System.ValueTuple`2' is not defined or imported
Compilation error (line 39, col 51): The name 'Name' does not exist in the current context
Thread Thread
elgoorf profile image
Hussein Duvigneau

Sorry, I was talking explicitly on JS terms

Thread Thread
dance2die profile image
Sung M. Kim

No worries, mate~ 😉

Collapse
tamas profile image
Tamás Szelei

I think it's worth pointing out that a lambda is not simply a shorthand for function declaration. IsAmy is a function. The other two variants are both lambdas and they differ from functions in that they capture all the locals and globals that are visible in the scope where the lambda is defined. IsAmy has no access to those.

Collapse
jonhilt profile image
Jon Hilton Author

Thanks for this, an important clarification and I appreciate you taking the time to outline it here :-)

Collapse
jorgecc profile image
Jorge Castro • Edited

I usually think of it as:

rows.somelinqcommand( row => some operation with the row);
// for example:
rows.filter( row => row.name=="john")

where the double arrow sometimes is pronounced as "then"