C
C#4w ago
Faker

✅ Anonymous and lambda function in C#

Hello guys, I was just reading a bit about lambda functions. From what I've understood, it's an inline style of declaring a function definition. We use it if for example we want to only execute a function once. They are used together with delegates, to store the function reference and just invoke the function as needed. Lambda function relies on delegates to work? E.g of lambda functions:
C#

Func<int,int> = (x) => {
return x * x;
};
C#

Func<int,int> = (x) => {
return x * x;
};
20 Replies
Thinker
Thinker4w ago
Lambdas and delegates are related but distinct. A delegate is a essentially the "type" of a method. It says "if you give me these arguments then I'll give you back a value", so it's a value you can pass around and potentially call with some arguments.
string Add(int a, int b)
{
return (a + b).ToString();
}

// arguments
// | | return
// v v v
Func<int, int, string> myFunc = Add;

Console.WriteLine(myFunc(1, 2)); // "3"
string Add(int a, int b)
{
return (a + b).ToString();
}

// arguments
// | | return
// v v v
Func<int, int, string> myFunc = Add;

Console.WriteLine(myFunc(1, 2)); // "3"
A lambda isn't necessarily reliant on the existence of a delegate, but in practice you have to use a delegate to be able to use a lambda. A lambda is just a normal method, except it doesn't have a name, you just declare it in-place. Since it doesn't have a name, the only way you can actually invoke it is by assigning it to a delegate and then invoking that.
Func<int, int, string> myFunc = (int a, int b) => (a * b).ToString();

Console.WriteLine(myFunc(2, 3)); // "6"
Func<int, int, string> myFunc = (int a, int b) => (a * b).ToString();

Console.WriteLine(myFunc(2, 3)); // "6"
Faker
FakerOP4w ago
yepp I see, thanks !
Thinker
Thinker4w ago
Also, you can actually assign a lambda to var and the compiler will figure out what delegate type it should be for you
// Func<int, int>
var a = (int x) => x + 1;

// Func<int, int, string>
var b = (int a, int b) => (a * b).ToString();

// Action<string>
var c = (string s) =>
{
Console.WriteLine(s);
};
// Func<int, int>
var a = (int x) => x + 1;

// Func<int, int, string>
var b = (int a, int b) => (a * b).ToString();

// Action<string>
var c = (string s) =>
{
Console.WriteLine(s);
};
Pobiega
Pobiega4w ago
Lambdas are super useful for letting the caller of a method "tweak" how it works. A classic example is the .Where method from LINQ
int[] numbers = [ 1, 2, 3, 4, 5 ];
var even = numbers.Where(x => x % 2 == 0).ToArray();
int[] numbers = [ 1, 2, 3, 4, 5 ];
var even = numbers.Where(x => x % 2 == 0).ToArray();
.Where takes a predicate, a function that takes in a T and returns a bool. In this case, T is int. it then calls the predicate function once for each value in the sequence. If the bool is false, it does nothing. if the bool is true, the value is returned as part of the resulting sequence from .Where
Faker
FakerOP4w ago
oh yeah, thanks ! ah I understood the syntax used in Select/Where method now, thanks ! by the way, why is it important to put the .ToArray() method ? why can't we omit it or if we omit it, what happen ? what happens behind the scenes? like even should be an array of even numbers but we didn't give it a size at first
FusedQyou
FusedQyou4w ago
This is required because LINQ methods, such as Where, work as an IEnumerable. These are special types which do not actually represent a collection, but rather a "blueprint" which then gets invoked by calling ToArray() or another method that generates a collection. The convenient thing is that you can chain LINQ methods and only when ToArray or something else is called, it iterates and allocates a collection. This is different compared to Javascript which has similar syntax, but each call makes a new collection. This is quite wasteful. You can make your own metod that returns IEnumerable. You'll see you have a new keyword yield that indicates a line that returns a collection entry, essentially. When you call the method, it invokes until a yield keyword. The returning IEnumerable can then be stepped through with special methods on the interface, such as moving to the next yield statement, until the code finishes and returns no more yield keywords. Alternatively you can also just call ToArray again. Note that methods such as ToArray does this exact system of stepping through an IEnumerable until no more entries are found. It's just a convenient method for it. Note the IEnumerable by itself doesn't necessarily define all this logic. It only defines the "enumerator" which is the thing you end up iterating (for example, this could be iterating your method). It's kind of a complex topic. You might want to give this a shot yourself https://learn.microsoft.com/en-us/dotnet/api/system.collections.ienumerable?view=net-9.0
Faker
FakerOP4w ago
yeah, in fact, while seeing this syntax, I compare it to javascript in my head, I thought they were doing the same thing, but this isn't the case?
FusedQyou
FusedQyou4w ago
The List is an example of a type implementing IEnumerable (it implements IList, which implements ICollection, which finally implements IEnumerable). It provides this enumerator: https://github.com/microsoft/referencesource/blob/master/mscorlib/system/collections/generic/list.cs#L1140 The syntax is weird, but note the method MoveNext and property Current. MoveNext moves the enumerator to the next "yield" which in this case is just the next index in the list since it's already a well-formed collection, and Current returns the current item pointed from the collection.
GitHub
referencesource/mscorlib/system/collections/generic/list.cs at mast...
Source from the Microsoft .NET Reference Source that represent a subset of the .NET Framework - microsoft/referencesource
FusedQyou
FusedQyou4w ago
LINQ's Select is Javascript's Map and LINQ's Where is Javascript's Filter It's just that all of Javascript's functions make a new array which is wasteful. They have something called iterators themselves which work the same as IEnumerables, though that's not used here
Faker
FakerOP4w ago
yeah, iterators are not the same thing as enumerators, I guess ?
FusedQyou
FusedQyou4w ago
Fun fact, they are actually considering the "pipeline" syntax which seeks to fix this exact issue: https://www.youtube.com/watch?v=k4xuEl6TmT4
Jack Herrington
YouTube
Javascript's New Pipeline Operator Is Awesome!
If you like .map, .filter, .reduce, etc. you'll love the proposed new pipeline operator. Proposal: https://github.com/tc39/proposal-pipeline-operator 👉 ProNextJS Course: https://pronextjs.dev 👉 Don't forget to subscribe to this channel for more updates: https://bit.ly/2E7drfJ 👉 Discord server signup: https://discord.gg/ddMZFtTDa5 👉 VS Code the...
FusedQyou
FusedQyou4w ago
They're actually very similar The issue is they exist on method level, and it's not something that can be extended to things like map and piped
Faker
FakerOP4w ago
Yep, will just read a bit about the IEnumerable and came back yep I see, will just do further readings and came back
FusedQyou
FusedQyou4w ago
Alrighty
Thinker
Thinker4w ago
JS becoming F#
Faker
FakerOP4w ago
Ok so I've read a bit. From what I've understood, something that implements the IEnumerable means that a sequence can be iterated over. It contains a single method GetEnumerator(). This returns an enumerator object that will allow us to enumerate through the sequence one at a time. The Enumerable sequence executes using "lazy execution" meaning each item in the collection is iterate one at a time (only when needed), we can pause/stop it whenever we want. (hmm I don't know if we can really pause it here, what I read is that an iterator remembers state (using yield return keyword internally I thing), but does this applies here?) Now concerning why we must use the ToArray(), it's because the Select/Where clause return an Enumerable; in order to have the result, we must explicitly convert them into a collection like the ToArray() method (in Javascript, this is done implicitly I guess like you said, so every time a new array is created and it's wasteful, like you said earlier.) here, the logic might be defined by another interfaces for example ? like when yield should stop something like that?
FusedQyou
FusedQyou4w ago
En enumerator just defines how you go through the source object and how it fetches the items it intends to return. You can "pause", but the whole system uses the two aforementioned methods (and Reset to reset iteration) so it's not like there's a start/end method. It's up to you You can't pause ToArray because it intends to efficiently create an array. Pausing would be silly. Otherwise you'd have to make your own enumerator if you want this.
Faker
FakerOP4w ago
yep I see
FusedQyou
FusedQyou4w ago
The interface does not define anything. It's kind of code magic in the background. Classes will implement the method, or you can return the interface in which case C# kind of does it for you in the background.
Faker
FakerOP4w ago
make sense now, I understood how all this work behind the scenes (just an overview but I understand now) with enumerator, iterators and collections that implement the enumerable interfaces, etc..., thanks !!

Did you find this page helpful?