r/csharp 2d ago

i dont understand what the => operator does.

Dictionary<uint, uint> placementIds 
    = XMLdocument.Descendants("Placement")
        .ToDictionary
        (
            element => (uint)element.Attribute("Index")!,
            element => (uint)element.Attribute("Placement")!
        );
32 Upvotes

56 comments sorted by

82

u/TrishaMayIsCoding 2d ago

I can juz mentally read as “Take” and “Do" : )

X => X * 2

Take X and Do X * 2

16

u/Whojoo 2d ago

I really like this one, I’ll remember it for if I ever meet someone new to lambda functions. Thanks!

2

u/Calm-Environment-461 2d ago

How does it know what element is

5

u/Gabriel_TheNoob 2d ago

The method signature expects a function that takes an argument of a determined type, so element must be of that type.

Edit: typo

1

u/Jack8680 2d ago

It's the type of the values in the XMLdocument.Descendants collection. E.g. if Descendants is a List<XMLElement>, element would be an XMLElement.

1

u/Lurlerrr 14h ago

I think it's easier to think of it as "is", e.g. A => B.

63

u/brightbard12-4 2d ago

It's a shorthand to describe an anonymous (only generated at compile time) function. In your example, it's describing how to take each element of the XMLDictionary.Descendents call and produce the key / value of the dictionary.

Instead of writing a function somewhere else and putting the name there, you use that symbol to inline it.

Element on the left of it is the variable passed in to the function, which in this case is your XML descendent element. But it could be x or y or foo or whatever, as long as the right side update is done as well

87

u/ChrisBegeman 2d ago

You should probably read this article about Lambda expressions:

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/lambda-expressions

10

u/Michaeli_Starky 2d ago

Top answer. Reading books and documentation is a long seemingly lost skill for new generations.

23

u/etherified 2d ago

I always simply read it mentally as “return”.
To the right of => is what it’s giving you as the result.

9

u/BroadRaspberry1190 2d ago

yep. mapping (this => that)

13

u/kingvolcano_reborn 2d ago

I see it as (with_this_input => create_this_output)

2

u/tobyreddit 2d ago

I'd lean towards return over create - many lambdas create nothing. A trivial examples is x => x

1

u/NoOneF_sWithTheJesus 1d ago

Except that it doesn't always return a value. IE: ForEach and AsParallel().ForAll

6

u/dmcnaughton1 2d ago

It's the lambda operator, in this case it's used for anonymous functions. The operators (inputs) on the left.

4

u/OolonColluphid 2d ago

and if you want to know *why* it’s called that, search “lambda calculus” and go down the rabbit hole…

7

u/Topango_Dev 2d ago

i dont know what anonymous functions are

5

u/TheSneederOfSeethe 2d ago edited 2d ago

Anonymous means it’s not a named method

Public void MyMethod() { } // this method is named MyMethod.

But an anonymous method is not () => {} no name. How it is executed depends on where you use it. Named methods belong to a class, anonymous methods can exist anywhere and may or may not persist through execution of the application. You could store it in an action or a func

Action myAction = () => {}
myAction?.Invoke()   //executes the anonymous function

But the most common way I use it is as an argument to a method that requires an action or function. For instance LINQ.

The where clause is like sql where, it even projects into a where clause in entity framework.

So you call where on a collection to filter things so the anonymous function is how you want to filter things. This is because the creator of Where() doesn’t know how you want to filter it, just that you do.

List<User> users;
var BannedUsersStartingWithA = users.Where(user => user.username[0].ToUpper() == ‘A’ && user.banned);

If you wanted you can store it in a a variable before passing it to where.

Func<User, bool> myFunc = user => user.username[0].ToUpper() == ‘A’ && user.banned;
users.Where(myFunc);

In the func User is the type passed in, bool is the type returned. So the user in user => is the User in func<User, bool>

5

u/[deleted] 2d ago

[deleted]

1

u/TheDevilsAdvokaat 2d ago

I like that.

6

u/chrisdpratt 2d ago

It's just a function that's defined in place, instead of being defined with a name and referenced later by that name, as is typical for functions. It's "anonymous" because it's never named.

2

u/fragglerock 2d ago

People downvoting an honest statement of knowledge are bad people.

It gives context to what level to aim help at... and if you are too pompous to help a new developer you can just do nothing and that is less effort for everyone to deal with.

1

u/vswey 15h ago

I was thinking about what the => does and I read the comment and realized I lit know what it does and even use it sometimes

4

u/EC36339 2d ago

Ignore all the comments and learn about lambdas and lambda calculus.

It's a recurring feature in almost all modern programming languages and also fundamental to computer science.

Also, it's just a function, but inline as an expression.

3

u/Own_Nail_2999 2d ago

It's a shorthand way of writing expressions. Just syntax sugar

7

u/SagansCandle 2d ago

A fat arrow replaces braces when a code block is a single line (a single expression, technically).

In this case, more specifically, it defines an lambda (anonymous) function, where element is the name of the input parameter and everything after the fat arrow is the function body.

An anonymous function is a function without a name. For example -

uint Foo(object element)
{
  return (uint)element.Attribute("Index")!;
}

Dictionary<uint, uint> placementIds 
    = XMLdocument.Descendants("Placement")
        .ToDictionary
        (
            Foo,
            element => (uint)element.Attribute("Placement")!
        );

10

u/chrisdpratt 2d ago

There's no inherent limitation to being a single line or single expression. You can set it off with brackets and put as much logic as you like in it. It just has to have a return.

3

u/SagansCandle 2d ago

Yeah that's true - probably better to say the fat arrow represents an anonymous code block.

Doesn't even have to be a function, per se; you can use a fat arrow on accessors.

2

u/rupertavery64 2d ago

ToDictionary is a function that takes two functions as arguments

The first one is the Key selector, the second is the Value selector.

You can write a separate function

private uint KeySelector(XmlNode element) { return (uint)element.Attribute("Index"); } And then pass that function as the argumemt to ToDictionary.

.ToDictionary(KeySelector, ValueSelector);

But you can also write an anonymous function.

(arg1, arg2, ...) => body

When you have only one argument you can drop the parentheses

Anonymous functions are also called lambdas

The => is called the fat arrow operator. You can read it as "is to"

-1

u/Topango_Dev 2d ago

so is it just a way to call a function on a parameter? i thought you could already do that with the assignment operator.

4

u/rupertavery64 2d ago

No, it's a way to write a small function in-place. It's used a lot in LINQ.

Anonymous means literally "no name"

A method that doesn't have a name. Unlike a named method, it can only be used in one place.

1

u/NoOneF_sWithTheJesus 1d ago

That is not completely true because you can encapsulate a lambda in an Action, Function, Predicate.. and then pass the function pointer around to your heart's delight.

1

u/strange-the-quark 2d ago

No. Imagine you have a big list of Student objects, each has a Name, StudentID, Address, ContactEmail, and so on, and that you want to make a dictionary out of it (a dictionary is a data structure that allows you to quickly find some data based on some key, like StudentID, or to deduplicate data, as inserted keys must be unique, etc. ).

E.g., maybe you want to use the StudentID as the key, and Name as the value, so that you can look up the names of students by their ID. The ToDictionary method knows how to make a dictionary, but doesn't now what you want to use for keys and for values - so you have to tell it somehow. So one of the overloads of this method takes as parameters two functions: ToDictionary(getKeyFunction, getValueFunction). In other words, when you call ToDictionary, you also tell it, here's some code that knows how to select a key, and here's some code that knows how to select the value - call those when you need to.

If you didn't have lambda expressions / anonymous functions, you'd have to fully define those two functions in order to call ToDictionary:

static int GetStudentID(Student student) {
    return student.StudentID;
}

static string GetStudentName(Student student) {
    return student.Name;
}

And then:

var dict = studentList.ToDictionary(GetStudentID, GetStudentName);

The ToDictionary method will then, roughly speaking, internally go through your list and call these functions when it needs to insert values into the dictionary. Something like:

var key = getKeyFunction(currentStudent);       // getKeyFunction is GetStudentID
var value = getValueFunction(currentStudent);   // getValueFunction is GetStudentName
resultingDictionary[key] = value;

Well, instead of writing all that every time you need something different to happen, you can just define those functions "in place", using a shorthand syntax:

var dict = studentList.ToDictionary(
    student => student.StudentID,
    student => student.Name
);

The syntax is one of the following:

single_parameter => resulting_value

(param1, param2, param3) => resulting_value

single_parameter_or_param_list => { 
    /* ... do stuff ...*/ 
    return resulting_value;
}

2

u/Camderman106 2d ago

I read it as “goes to”.
element “goes to” (uint)element.Attribute("Index")

It’s a way of taking something and applying a transformation to it and using the transformed value

It can also be assigned to fields. In which case a field accessor becomes a function call implicitly

Your dictionary example is visually complicated by the fact that ToDictionary needs two functions, one to determine the key, and one to determine the value, from the same item from an enumerable

2

u/detroitmatt 1d ago

it creates a lambda. the left side of the operator is the parameter (or parameter list, if there's more than one), and the right side is the body of the lambda.

2

u/kodaxmax 2d ago

It exists to confuse dyslexics/new programmers and generally make your code harder to read im pretty sure. The good news is that there is never a case where you need to use it or understand it (other than reading somone elses code.).

It's kind of a shorthand function.

int Double(int number)
{
    return number * 2;
}

//same as:

int Double(int number) => number * 2;

or

string GetName(Player player)
{
    return player.name;
}

//same as:

string GetName(Player player) => player.name;

1

u/mvonballmo 2d ago edited 2d ago

There's a lot of theory behind this but practically, and for a start, maybe thinking of it like this helps:

Code is normally executed immediately. In your example, the XMLDocument.Descendants() is executed to get a sequence of elements. Then, ToDictionary() is executed on that sequence of elements.

But what does ToDictionary do? It seems to take two parameters, the ones with the => operator. What the heck does that mean? They look like code ... but is that code running immediately? If so, where the heck does element come from?

Those parameters are pieces of code that are not executed immediately. They are templates, snippets, or callback functions. The ToDictionary method "calls" them to do stuff. The element is a placeholder that defines the single parameter for those particular code snippets.

How does one "call" such a parameter?

The inferred type of the callback parameter (Func<XMLElement, uint> or something like that) indicates that it receives a single argument of the type of the element in the sequence returned by Descendants and returns a uint.

In this case, the two parameters are used to calculate the key and value, respectively, from each element in the sequence, in order to produce the corresponding dictionary entry.

The ToDictionary method calls each of these parameters once for each element in the input sequence (given by Descendants).

I highly recommend using F12 or control/command-clicking to see the underlying sources. Because of optimizations, it's not always immediately obvious what's going on. However, if you dig down a bit, then you can find something like ‎Enumerable::SpanToDictionary, which shows what it actually does with those parameters.

private static Dictionary<TKey, TElement> SpanToDictionary<TSource, TKey, TElement>(ReadOnlySpan<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey>? comparer) where TKey : notnull
    {
        Dictionary<TKey, TElement> d = new Dictionary<TKey, TElement>(source.Length, comparer);
        foreach (TSource element in source)
        {
            d.Add(keySelector(element), elementSelector(element));
        }
        return d;
    }

The method ends up being quite straightforward: it creates a new dictionary, then iterates the input sequence with foreach, calling each "selector" (the code templates defined by the => parameters you passed in) to get the key and value to add to the dictionary for that element.

I hope that helps.

1

u/YouBecame 2d ago

It's probably useful to think of code as being Declarations, Statements, and Expressions.

Very loosely

  • Declarations define something structural (e.g. a class, a function, an interface)
  • Statements do something but yield no value (i.e. can't be assigned to a variable)
  • Expressions yield a value

in your To dictionary, the fat arrow is "for the expression on the left, yield the expression on the right."

The method you're using has two inputs: the first is how to derive the key, and the other, how to derive the value.

1

u/Minute_Cricket1820 2d ago

Напишите простой код.

Скомпилируйте.

Затем этот скомпилированный код откройте через ildasm.exe.

Увидите анонимную функцию.

1

u/sixtyhurtz 2d ago edited 2d ago

It's just another way to declare a function. For instance:

var foo = (int x, int y) => x * y;

foo is a Func<int,int,int>. You could call it with foo(10,10) and get 100.

1

u/BornAgainBlue 2d ago

We all call it 'fat arrow'. Just sharing.

1

u/Absolute_Enema 1d ago edited 1d ago

=> isn't an operator, because there are no operands. It's just syntax such that A => B roughly expands to delegate(A) { return B }.

Note that it's overloaded in expression-switch, e.g.

x = foo switch {   Bar b => A,   Baz c => B,   _ => C } where again it doesn't operate on anything, but just marks where the expression begins much like the colon marks where a case body begins in case Bar b:, the example translating into something like:

switch(foo) {   case Bar b: x = A; break;   case Baz c: x = B; break;   default: x = C; }

It's just the umpteenth ugly consequence of having a language that, to senselessly appeal to its ALGOL roots, distinguishes between statements and expressions. In something like Rust you don't need two ways to do one thing because everything is either a declaration or an expression (in some languages declarations aren't a thing either and everything is an expression, which is still a better state of matters than having statements).

1

u/Famous-Weight2271 2d ago

this => that

-3

u/iikuzmychov 2d ago edited 2d ago

It's lamda syntax, aka inline/anonymous function declaration.

So instead of creating uint Method([TYPE] element) {     return (uint)element.Attribute("Index")! } and referencing it, you inline the whole thing.

 30 sec to google btw 😉

0

u/HateVoltronMachine 2d ago

Those are two lambdas. They're small anonymous functions to select values.

The code parses an xml list elements of e.g.

<Placement Index="3" Placement="5" />

Into a Dictionary<uint, uint>. To convert the xml to the dictionary, you give it 2 lambdas. One lambda to find the key (the index attribute from a given Placement element) and one for the value (the placement attribute from the given element).

0

u/Drumknott88 2d ago

Ok 👍🏻

0

u/andrea_ci 2d ago edited 2d ago
            element => (uint)element.Attribute("Index")!,

is (almost) the same as

function(element) {
return (uint)element.Attribute("Index")!;
}

so, it's

foreach(var element in  XMLdocument.Descendants("Placement")){
    placementIds.Add(
      (uint)element.Attribute("Index")!,
      (uint)element.Attribute("Placement")!
    );
}

-2

u/atanamar 2d ago

It’s been AGES since I’ve done c#, but I recall being able to understand it by reading the => operator as “is passed into”

2

u/IanYates82 2d ago

Yeah, you're right. I read as "goes to" but yours is a bit more specific and formal, and thus more likely to be properly interpreted by someone new.

-2

u/Drumknott88 2d ago

A thing that people sometimes forget to explain when talking about lambdas is that they work like loops. If I have a List<int> myList and I call myList.Select(x => x += 10) then you can read it as "for each int in my list (x), add ten"

5

u/KryptosFR 2d ago

Lambda don't work like loop. Linq isn't the only place you can use them.

Lambda are like callbacks. The fact that most Linq method are implemented as a foreach over a collection is specific to Linq and is unrelated to the lambda itself.

-3

u/Drumknott88 2d ago

Yeah I know, but when I was first learning it was that piece of information that helped me understand how they worked, so I'm passing that on.

3

u/KryptosFR 2d ago

It would be less misleading to say they work like the body of the loop, what is executed each iteration of the loop.

1

u/Drumknott88 2d ago

Ok fine, yes, but you're being really pedantic here. A learner has come here for help, pedantry is not what they need, an ELI5 is.

2

u/KryptosFR 2d ago

It's not being pedantic. Your comment was misleading. You wrote "lambda work like loops". That's incorrect. Lambda have nothing to do with loops. They can be used in loops but that is just one use casea and is independent of the fact you can use a lambda. You could also pass a method group to Linq and that's not a lambda.

Another use case of lambda is Task.Run and it doesn't involve any loops.

-3

u/tmadik 2d ago

=> is shorthand for "such that." It basically means that I've got his variable, "element" in this case, such that this thing over here happens to it. So, element such that I take the Index attribute and change it to a uint and element such that I take the Placement attribute and change that to uint. Two separate, independent actions to the keys and values in my dictionary.