C
C#ā€¢2y ago
surwren

Need help understanding lambda statements and delegates

I'm extremely confused about the syntax and it's hemorraging my ability to work on other stuff (LINQ, middleware) How do I even begin comprehending the syntax? I keep rereading the MSDN documentation and watching youtube videos and nothing makes sense
108 Replies
ero
eroā€¢2y ago
What exactly is confusing you? You can write any lambda as a full method, if that helps?
Func<string, int> parse = input => int.Parse(input);
Func<string, int> parse = input => int.Parse(input);
int Parse(string input)
{
return int.Parse(input);
}
int Parse(string input)
{
return int.Parse(input);
}
canton7
canton7ā€¢2y ago
More details please. What exactly is confusing you?
surwren
surwrenā€¢2y ago
Yea so why does this look utterly different from the middleware lambda?
app.Use(async (context, next) =>...
app.Use(async (context, next) =>...
I know a lambda basically lacks a method name It's just parameters => dosomething
ero
eroā€¢2y ago
async Task<PotentialReturnType> Lambda(ContextType context, NextType next)
{
// lambda body
}
async Task<PotentialReturnType> Lambda(ContextType context, NextType next)
{
// lambda body
}
surwren
surwrenā€¢2y ago
But why is the syntax so drastically different between lambdas used in middleware, delegate, and other functions?
ero
eroā€¢2y ago
different in what way?
surwren
surwrenā€¢2y ago
Func<string, int> parse = input =>
Func<string, int> parse = input =>
app.use (async (context,next) =>
app.use (async (context,next) =>
The parameters aren't even lined up the same way I don't get it In the first one the return type is int and input is string But they are both inside of func <> And then in the second, both context and next are input parameters and there is no return type specified in the lambda Why is there no consistency between the syntaxes? There are no patterns Or at least my stupid brain can't see the patterns I can only recognize that => indicates to dosomething But i keep getting tripped up by the lambda signature It seems that the signature changes every time it's used and it drives me crazy because I can't figure out why Ok for example
parse=input
parse=input
what does that even mean?
ero
eroā€¢2y ago
parse is the lambda's name just like any other variable int myInt = 0; string myString = ""; i could have named it myLambda if you prefer that input is the string parameter's name just like in the full method body below string input i could have wrapped input in parens if you prefer that Func<string, int> myLambda = (input) => int.Parse(input); what you're using in app.Use() is an anonymous function, one which doesn't have a name
surwren
surwrenā€¢2y ago
But why is input even there, and missing a type declaration?
ero
eroā€¢2y ago
so all that remains is (input) => int.Parse(input); the type declaration is in the Func type Func<string, int> takes in one string parameter, and returns one integer
surwren
surwrenā€¢2y ago
So the return type of a lambda is always the last value of the
Func
Func
declaration?
ero
eroā€¢2y ago
indeed
surwren
surwrenā€¢2y ago
Func<int, int, bool> testForEquality = (x, y) => x == y;
Func<int, int, bool> testForEquality = (x, y) => x == y;
Return would be bool
Func<int,int[],List<int>,List<int>>
Func<int,int[],List<int>,List<int>>
Return would be List<int>?
ero
eroā€¢2y ago
mhm
surwren
surwrenā€¢2y ago
So then the variable names (x, y) correspond in order with the int,int declarations in Func (for the first lambda)?
ero
eroā€¢2y ago
yup
surwren
surwrenā€¢2y ago
If it was Func<int,string,int> trial = (a,b)=> It would be int a, string b
ero
eroā€¢2y ago
yup
surwren
surwrenā€¢2y ago
I still dont get this
ero
eroā€¢2y ago
you can actually declare them explicitly
Func<int, string, int> foo = (int a, string b) => { };
Func<int, string, int> foo = (int a, string b) => { };
surwren
surwrenā€¢2y ago
Ah But it needs to match the Func<> declaration?
ero
eroā€¢2y ago
yeah
surwren
surwrenā€¢2y ago
Im still having trouble mapping the app.use one in my head
ero
eroā€¢2y ago
well, just like you don't need to do
string foo = "Hello, world!";
Console.WriteLine(foo);
string foo = "Hello, world!";
Console.WriteLine(foo);
but instead can just skip the variable declaration;
Console.WriteLine("Hello, world!");
Console.WriteLine("Hello, world!");
the same goes for lambdas
Func<int, string, int> foo = (bar, baz) => int.Parse(baz) - bar;

SomeMethodThatTakesALambdaOfTheAboveSignature(foo);
Func<int, string, int> foo = (bar, baz) => int.Parse(baz) - bar;

SomeMethodThatTakesALambdaOfTheAboveSignature(foo);
SomeMethodThatTakesALambdaOfTheAboveSignature((bar, baz) => int.Parse(baz) - bar);
SomeMethodThatTakesALambdaOfTheAboveSignature((bar, baz) => int.Parse(baz) - bar);
surwren
surwrenā€¢2y ago
Ohhh so you excluded the
foo
foo
because inserting the lambda directly into the function doesn't require you to give the lambda a variable name But async keyword remains?
ero
eroā€¢2y ago
the async is there because the return type of your lambda is a Task or a Task<T> (depending on if it returns anything) and Tasks can be awaited
surwren
surwrenā€¢2y ago
I have no idea what a Task<T> is
ero
eroā€¢2y ago
you're gonna have to learn that if you're gonna wanna do web dev
surwren
surwrenā€¢2y ago
Oh msdn documentation shows you can do Task<int>
ero
eroā€¢2y ago
T would be int in that case, yes
surwren
surwrenā€¢2y ago
I feel like I need a whole different thread for clearing up understanding on Tasks Ik Task is a return type in itself But thats sort of digressimg Ok back to lambdas
app.Use(async (context, next) =>
{
if (context.Request.Path.StartsWithSegments("/Login/"))
{
await next(context);
return;
}

string sessionId = context.Request.Cookies["sessionId"];
if (sessionId == null)
{

context.Response.Redirect("/Login/");
}
else
{
await next(context);
}
});
app.Use(async (context, next) =>
{
if (context.Request.Path.StartsWithSegments("/Login/"))
{
await next(context);
return;
}

string sessionId = context.Request.Cookies["sessionId"];
if (sessionId == null)
{

context.Response.Redirect("/Login/");
}
else
{
await next(context);
}
});
In this case the entire {} chunk is the dosomething part right
ero
eroā€¢2y ago
yeah
surwren
surwrenā€¢2y ago
And the entire lambda is just thrown into app.use Hmm Lemme grab the delegates code i was confused about for 2 months Gimme a bit ok //defining a new datatype called IntOps that represent method that accept int and return an int
delegate int IntOps(int n);
delegate int IntOps(int n);
//Once we declare this, we can create a method that uses this delegate type
void ApplyOperation(int[] arr, IntOps ops)
void ApplyOperation(int[] arr, IntOps ops)
//we can use the parameter just like a normal method in our method
static void ApplyOperation(int[] arr, IntOps ops){
for (int i=0; i<arr.Length; i++) {
arr[i] = ops(arr[i]);
}
}
static void ApplyOperation(int[] arr, IntOps ops){
for (int i=0; i<arr.Length; i++) {
arr[i] = ops(arr[i]);
}
}
//method Add10 match the signature of the IntOps delegate //I don't understand what this means, or why
static void Main() {
int[] A = new int[] {1, 2, 3};
IntOps myOp = Add10;
ApplyOperation(A, myOp);
//A should contain {11, 12, 13} now.
}
static int Add10(int x) {
return x + 10;
}
static void Main() {
int[] A = new int[] {1, 2, 3};
IntOps myOp = Add10;
ApplyOperation(A, myOp);
//A should contain {11, 12, 13} now.
}
static int Add10(int x) {
return x + 10;
}
//Calling the method that accepts a delegate (using anonymous method)
static void Main() {
int[] A = new int[] {1, 2, 3};
IntOps myOp = delegate(int a)
{
return a + 10;
};
ApplyOperation(A, myOp);
//A should contain {11, 12, 13} now.
}
static void Main() {
int[] A = new int[] {1, 2, 3};
IntOps myOp = delegate(int a)
{
return a + 10;
};
ApplyOperation(A, myOp);
//A should contain {11, 12, 13} now.
}
//Calling the method that accepts a delegate (using delegate)
static void Main() {
int[] A = new int[] {1, 2, 3};
IntOps myOp = (int a) => { return a + 10; };
ApplyOperation(A, myOp);
//A should contain {11, 12, 13} now.
}
static void Main() {
int[] A = new int[] {1, 2, 3};
IntOps myOp = (int a) => { return a + 10; };
ApplyOperation(A, myOp);
//A should contain {11, 12, 13} now.
}
Ig here my issue is, what is an anonymous function and what does it have to do with lambdas? Ok so if we compare the one I just posted to yours (with explicit declaration)
IntOps myOp = (int a) => { return a + 10; };
IntOps myOp = (int a) => { return a + 10; };
Func<string, int> parse = (string input) => { int.Parse(input) };
Func<string, int> parse = (string input) => { int.Parse(input) };
Oh so it's replacing the Func<> declaration with IntOps myOp But why? What even is IntOps myOp and why does it apply a lambda
surwren
surwrenā€¢2y ago
Ok, so
delegate int IntOps(int n);
delegate int IntOps(int n);
Is declaring a delegate that returns int, of name IntOps, that accepts parameter int n https://www.tutorialsteacher.com/csharp/csharp-delegates Based on the graphic in this thread
surwren
surwrenā€¢2y ago
--- Oh so it's just showing 3 different ways of assigning a method to
IntOps myOp
IntOps myOp
First way is by assigning the method name directly (Add10) Second way is by assigning an anonymous method (I don't really get this) Third way is assigning using a lambda === Ok so in some videos they refer to lambda functions as anonymous functions. But it seems that in this code example they are not the same? Anonymous function example
IntOps myOp = delegate(int a)
{
return a+10
}
IntOps myOp = delegate(int a)
{
return a+10
}
Lambda expression example
IntOps myOp = (int a) => { return a+10; };
IntOps myOp = (int a) => { return a+10; };
@Ero are anonymous functions the same thing as lambdas? If they are, why does the syntax differ?
canton7
canton7ā€¢2y ago
There is old and new syntax: old: delegate (int x) { return ...;}, new: x => ... The => syntax is called lambda syntax
surwren
surwrenā€¢2y ago
So what is the delegate(int x) syntax called
canton7
canton7ā€¢2y ago
Have you played with sharplab?
surwren
surwrenā€¢2y ago
Nope
canton7
canton7ā€¢2y ago
It's probably worth playing with. https://sharplab.io
SharpLab
C#/VB/F# compiler playground.
canton7
canton7ā€¢2y ago
See what the different syntaxes generate They all end up generating a normal C# method under the hood
surwren
surwrenā€¢2y ago
That would be great for actually learning yeah but I'm trying to understand what the delegate(int x) syntax is called Does it have a name?
canton7
canton7ā€¢2y ago
I don't know if it had a specific name. You used the syntax to declare what was called an anonymous function mainly, iirc. There are lots of names for the same things
surwren
surwrenā€¢2y ago
in my notes it's labelled an anonymous function in the videos i've seen they refer to lambdas as anonymous functions. Are anonymous functions == lambdas or are anonymous functions an umbrella term that includes lambdas?
canton7
canton7ā€¢2y ago
Iambdas, anonymous methods, closures, all effectively the same thing
surwren
surwrenā€¢2y ago
If they are the same thing then why does the syntax not have => Why does it have delegate() instead Does this mean delegate() is a method used to designate a lambda/anon method/closure?
canton7
canton7ā€¢2y ago
strictly speaking in C#, the => syntax is a lambda. You can use that to declare an anonymous function/closure, but you can also use => to declare a linq expression (though that's an added complication you can probably ignore for now) The old and new syntaxes do the same thing
surwren
surwrenā€¢2y ago
Oh yeah I did see linQ expressions with ()=> I wanted to ask about those too I was really confused when I first saw them
canton7
canton7ā€¢2y ago
The complication there linq is in two parts: you can use the same => syntax to declare both an inline method, and something which an be translated to SQL. But let's ignore that for now
surwren
surwrenā€¢2y ago
How do I differentiate between the two?
canton7
canton7ā€¢2y ago
All these do the same thing:
public int Method(int x) { return x * 2; }
Func<int, int> doubler = new Func<int, int>(Method);
Func<int, int> doubler = Method;
Func<int, int> doubler = delegate(int x) { return x*2; };
Func<int, int> doubler = x => x * 2;
public int Method(int x) { return x * 2; }
Func<int, int> doubler = new Func<int, int>(Method);
Func<int, int> doubler = Method;
Func<int, int> doubler = delegate(int x) { return x*2; };
Func<int, int> doubler = x => x * 2;
In the first two, we're writing a method ourselves and creating the delegate instance doubler from that method. In the latter two, we're using different syntax to write the method inline, and the compiler is creating the method for us under the hood, and then creating the delegate instance from that method You don't see the 3rd syntax any more -- the 4th one is a replacement which is better
surwren
surwrenā€¢2y ago
Whats the purpose of doing = new Func<int,int>(Method) if = Method achieves the same thing?
canton7
canton7ā€¢2y ago
The way of writing the method inline is variously called a closure, lambda, inline method, anonymous delegate... All basically the same thing, coming from different languages and paradigmes = new DelegateType(Method) came first, then = Method was added later as a shorthand to reduce the boilerplate. It's still very occasionally useful = Method still generates new DelegateType(Method) under the hood
surwren
surwrenā€¢2y ago
Func<> is new DelegateType? Why does c# name the methods relating to lambdas things that begin with 'delegate..'? I thought delegates and lambdas were separate concepts
canton7
canton7ā€¢2y ago
Yes. The framework declares it as public delegate TResult Func<in T, out TResult>(T arg);: https://source.dot.net/#System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/Function.cs,7 A delegate is its own thing, similar to but separate from a class and struct. A delegate is a thing which points to a method, and can be used to invoke the method. You declare the delegate type with delegate returntype DelegateType(param1, param2, ...) (and the type describes the delegate's parameters and return type), and instantiate it with new DelegateType(method) (although as we've seen, the compiler will add the instantiation for you to reduce boilerplate most of the time)
surwren
surwrenā€¢2y ago
Oh so lambdas are just one way of assigning a method to a delegate/method pointer?
canton7
canton7ā€¢2y ago
They're a way of declaring a method inline, and creating a new delegate instance which points to that method, yes
surwren
surwrenā€¢2y ago
Are delegates ref type or value type? Pointer seems to indicate ref type
canton7
canton7ā€¢2y ago
They're immutable reference types Confusingly, they can be combined: SomeDelegate result = delegate1 + delegate2. Invoking result will invoke both delegate1 and delegate2. Events are built around that bit of functionality, although you don't see it used outside of events
surwren
surwrenā€¢2y ago
I haven't covered events yet, probably need to cover linQ when I wake up in the morning Will be back with more questions about lambda syntaxes but in linq
canton7
canton7ā€¢2y ago
https://sharplab.io/#v2:EYLgHgbALANAJiA1AHwAICYCMBYAUKgZgAIMiBhIgbzyNpONSiIFkAKASiprp9QFYAPAEsAdgBcYRUWIB8ROAHsArsAA2AUwBORALxEwuuQYBURdAG5utAL55rQA. See there. There's some extra stuff around caching the delegate instance, but you should be able to see what's goind on
SharpLab
C#/VB/F# compiler playground.
surwren
surwrenā€¢2y ago
Wdym by caching šŸ˜³
canton7
canton7ā€¢2y ago
It put the method x * 2 (which it called <M>b__0_0) on a new generated inner class called <>c, and then did new Func<int, int>(<>c.<>9.<M>b__0_0) So if the code Func<int, int> doubler = x => x * 2; is run lots of times, it only needs to create one delegate instance, which makes it cheaper https://en.wikipedia.org/wiki/Cache_(computing)
surwren
surwrenā€¢2y ago
Oh you mean the compiler adding the delegate instantiation boilerplate and then caching the resulting delegate Bruh
canton7
canton7ā€¢2y ago
yep
surwren
surwrenā€¢2y ago
Guh I wish I could add you so it would be easier to reach than pinging you in this thread šŸ„² Ah, so this
IntOps myOp = (int a) => { return a + 10; };
IntOps myOp = (int a) => { return a + 10; };
requires IntOps to define the return and input param type in
delegate int IntOps(int n)
delegate int IntOps(int n)
because there's no Func<int,int> definition
static void ApplyOperation(int[] arr, IntOps ops){
for (int i=0; i<arr.Length; i++) {
arr[i] = ops(arr[i]);
}
}
static void ApplyOperation(int[] arr, IntOps ops){
for (int i=0; i<arr.Length; i++) {
arr[i] = ops(arr[i]);
}
}
And then this code just accepts the lambda assigned to IntOps as an input parameter, which it then manipulates int[] arr with === Then I have a question, what is the difference between these two?
Func<int,int> SomeMethod
Func<int,int> SomeMethod
and
delegate int SomeMethod (int n)
delegate int SomeMethod (int n)
you can assign a corresponding lambda statement to both of these. What purpose does each serve?
Kouhai
Kouhaiā€¢2y ago
GitHub
runtime/Function.cs at 57bfe474518ab5b7cfe6bf7424a79ce3af9d6657 Ā· d...
.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps. - runtime/Function.cs at 57bfe474518ab5b7cfe6bf7424a79ce3af9d6657 Ā· dotnet/runtime
surwren
surwrenā€¢2y ago
Both work when inserted into
void ApplyOperation(int[] arr, Func<int, int> ops) ...
void ApplyOperation(int[] arr, Func<int, int> ops) ...
void ApplyOperation(int[] arr, IntOps ops) ...
void ApplyOperation(int[] arr, IntOps ops) ...
Just that to use a delegate function, you need another line
delegate int IntOps(int n);
delegate int IntOps(int n);
lol it supports up to a max of 16 input params
Kouhai
Kouhaiā€¢2y ago
Yeah šŸ˜…
surwren
surwrenā€¢2y ago
well the Func<> syntax certainly seems more convenient considering that you don't need to declare a
delegate returntype methodname(input params)
delegate returntype methodname(input params)
but is there any advantage to using the delegate int IntOps(int n) method at all?
Kouhai
Kouhaiā€¢2y ago
I don't know about others, but I personally use Func<> or Action<> most of the times unless I want to have a type name.
surwren
surwrenā€¢2y ago
wdym by type name
Kouhai
Kouhaiā€¢2y ago
Very rarely you might use it for ref, out because Func and Action don't allow that
surwren
surwrenā€¢2y ago
you mean like
delegate int IntOps(ref int n)
delegate int IntOps(ref int n)
?
Kouhai
Kouhaiā€¢2y ago
Basically instead of public void Test(Func<int, int, int, string> callback) public delegate string CallbackDelegate(int param0, int param1, int param2) and public void Test(CallbackDelegate callback) Yeah, with Func and Action you can't ref and out
surwren
surwrenā€¢2y ago
oh
surwren
surwrenā€¢2y ago
gdi are you talking about delegate name? I still don't quite understand
Kouhai
Kouhaiā€¢2y ago
Oh sorry, I didn't clarify, yes I was taking about the the delegate name It might be easier for the caller to understand the purpose of it. Or it might save you typing extra characters if you use that delegate a lot in your code
surwren
surwrenā€¢2y ago
I also have a question- when using top level statements (TLS), it seems that the delegate variable holding the relevant lambda can be declared at any point in the code (before or after the method calling it) Why is this possible? Whereas other variable declarations must occur in the proper scope ahead of the TLS call
Kouhai
Kouhaiā€¢2y ago
Wdym, I don't think you can call it before the variable is declared šŸ¤”
surwren
surwrenā€¢2y ago
I'll screengrab when I get back to the computer How would I go about looking for the definitions of app.Use from the original asp repo?
Kouhai
Kouhaiā€¢2y ago
app?
surwren
surwrenā€¢2y ago
Middlewares
Kouhai
Kouhaiā€¢2y ago
Oh Here's the default implementation for IApplicationBuilder
Kouhai
Kouhaiā€¢2y ago
GitHub
aspnetcore/ApplicationBuilder.cs at 51fc6824e30016404964fc96a4a098a...
ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux. - aspnetcore/ApplicationBuilder.cs at 51fc6824e30016404964fc96a4a098a764b...
surwren
surwrenā€¢2y ago
it's callable
Kouhai
Kouhaiā€¢2y ago
It's declared before getting called, no?
surwren
surwrenā€¢2y ago
wait if that's the declaration then what does this do
delegate int Testing(int n);
delegate int Testing(int n);
Kouhai
Kouhaiā€¢2y ago
Oh, that's the type declaration, this is similar to class MyClass {}
surwren
surwrenā€¢2y ago
ohhh is it correct to call it the class declaration? the syntax is green indicating that Testing it's a class/object
Kouhai
Kouhaiā€¢2y ago
well, it's a delegate declaration not a class
surwren
surwrenā€¢2y ago
what does green coloration in the IDE indicate object?
Kouhai
Kouhaiā€¢2y ago
I think with the default visual studio config it indicates a Type
surwren
surwrenā€¢2y ago
surwren
surwrenā€¢2y ago
so in other word, classes and delegates are types?
surwren
surwrenā€¢2y ago
Kouhai
Kouhaiā€¢2y ago
A class is a type, let's think of it that way when you do class MyClass {} And instantiate it like this var myClassInstance = new MyClass() what's the type of myClassInstance?
surwren
surwrenā€¢2y ago
but if you assign a variable to a type, it's not green
Kouhai
Kouhaiā€¢2y ago
Try Int32 iUse = 10 What is it's color?
surwren
surwrenā€¢2y ago
Green, but it's a different shade from the object/delegate green
surwren
surwrenā€¢2y ago
surwren
surwrenā€¢2y ago
surwren
surwrenā€¢2y ago
Int32 is a struct
Kouhai
Kouhaiā€¢2y ago
Yup, it's a struct, and the reason it has a darker shade is visual studio is suggesting to substitute it with int int is a keyword in c#, that keyword map directly to the Type Int32
surwren
surwrenā€¢2y ago
oh so blue indicates keywords
Kouhai
Kouhaiā€¢2y ago
That's right šŸ‘
surwren
surwrenā€¢2y ago
that's so fascinating wtf why are read only types like int considered keywords?
Kouhai
Kouhaiā€¢2y ago
because they are defined as keywords in ECMA-334 (the standard specs for C#) And it's more intuitive to just do int short instead of Int32 Int16
Want results from more Discord servers?
Add your server
More Posts