C
C#3y ago
surwren

Assigning Lambda to Delegate (PurposesReason)

Based on my (possibly incorrect) understanding, delegates are the variables to which lambda expressions are assigned. https://stackoverflow.com/a/73819 Also based on my understanding (possibly incorrect), assigning a lambda to a delegate in this way makes it possible to pass said delegate/lambda expression into another method as an input parameter. My question is, what are the advantages of passing lambda logic into other method with delegates? Is it solely to promote reusability of code? Or are there other advantages to it?
Stack Overflow
What is the difference between lambdas and delegates in the .NET Fr...
I get asked this question a lot and I thought I'd solicit some input on how to best describe the difference.
6 Replies
Kouhai
Kouhai3y ago
Are you asking about the reason to use delegates or lambdas?
mtreit
mtreit3y ago
lambdas can also capture state from the scope in which they are declared (a "closure") which can be useful.
333fred
333fred3y ago
In general, it's useful to pass code around as an abstraction. Consider everyone's favorite functional code, LINQ. If you had to re-implement Select and Where and all the rest of LINQ for every type of IEnumerable, you'd be a very sad programmer
Stroniax
Stroniax3y ago
Lambdas let you write a function inline without defining an actual, full method to do that work. Assigning a lambda to a delegate lets you reuse the same delegate instance multiple times, which is useful for example with event subscriptions. This is pseudocode and probably wouldn't actually compile, but I find that wrapping the event-based async pattern with the task-based async pattern is a great use for this.
public static async Task<PingResult> SendPingAsync(this ComputerDto computer, CancellationToken cancellationToken = default) {
var tcs = new TaskCompletionSource<PingResult>();

// I assign the lambda expression to the pingHandler delegate
// this lets me use += and -= with this handler, without needing a named method that I can pass in
// which is good, because my lambda creates a closure by referencing a variable in the method scope
PingCompletedEventHandler pingHandler = (sender, args) => tcs.SetResult(args);
using var ping = new Ping();
// register cancellation using a lambda, which in this case is implicitly converted to a delegate for the register callback
using var creg = cancellationToken.Register(() => {
ping.CancelAsync();
tcs.SetCancelled();
}
// register the event handler delegate
ping.Completed += pingHandler;
// starts ping; result only comes back from event
ping.SendAsync(computer.IPAddress);

try {
return await tcs.Task;
}
finally {
// this only works because the event handler delegate is the same instance I subscribed
// realistically since I'm disposing of the ping, this isn't necessary
// but you see how I can unregister the delegate which I wouldn't be able to do otherwise
// with just a lambda expression again here
ping.Completed -= pingHandler;
}
}
public static async Task<PingResult> SendPingAsync(this ComputerDto computer, CancellationToken cancellationToken = default) {
var tcs = new TaskCompletionSource<PingResult>();

// I assign the lambda expression to the pingHandler delegate
// this lets me use += and -= with this handler, without needing a named method that I can pass in
// which is good, because my lambda creates a closure by referencing a variable in the method scope
PingCompletedEventHandler pingHandler = (sender, args) => tcs.SetResult(args);
using var ping = new Ping();
// register cancellation using a lambda, which in this case is implicitly converted to a delegate for the register callback
using var creg = cancellationToken.Register(() => {
ping.CancelAsync();
tcs.SetCancelled();
}
// register the event handler delegate
ping.Completed += pingHandler;
// starts ping; result only comes back from event
ping.SendAsync(computer.IPAddress);

try {
return await tcs.Task;
}
finally {
// this only works because the event handler delegate is the same instance I subscribed
// realistically since I'm disposing of the ping, this isn't necessary
// but you see how I can unregister the delegate which I wouldn't be able to do otherwise
// with just a lambda expression again here
ping.Completed -= pingHandler;
}
}
surwren
surwrenOP3y ago
Kind of both actually, I'm not too clear on the distinction
Kouhai
Kouhai3y ago
@surwren Adding to all amazing answers A delegate is just a type can hold a reference to a method, do give an example of why a delegate would be useful take this example
public delegate void OnClick(int x, int y);

public class Button
{
private OnClick _onClick;
public Button(OnClick onClick)
{
_onClick = onClick;
}
internal void PerformeClick(int x, int y)
{
...... // Some internal code here
_onClick(x, y);
...... // Some internal code here
}
}

public class UI
{
private Button _start;
private Button _stop;
public UI
{
_start = new Button(OnStart);
_stop = new Button(OnStop);
}

private void OnStart(int x, int y)
{
Console.WriteLine("Start!");
}
private void OnStop(int x, int y)
{
Console.WriteLine("Stop!");
}
}
public delegate void OnClick(int x, int y);

public class Button
{
private OnClick _onClick;
public Button(OnClick onClick)
{
_onClick = onClick;
}
internal void PerformeClick(int x, int y)
{
...... // Some internal code here
_onClick(x, y);
...... // Some internal code here
}
}

public class UI
{
private Button _start;
private Button _stop;
public UI
{
_start = new Button(OnStart);
_stop = new Button(OnStop);
}

private void OnStart(int x, int y)
{
Console.WriteLine("Start!");
}
private void OnStop(int x, int y)
{
Console.WriteLine("Stop!");
}
}
When PerformeClick is called on _start OnStart get's invoked, is really helpful because now you can make execute your own code when a Button is clicked, or you can swap what's getting executed (in the case of the example about, that can't happen because Button._onClick is private and can't be set anywhere but I hope you get the point) Mow for lambdas, they are anonymous functions (and can be closures) let's take another example
public delegate bool Predicate(string str);

public static class WordList
{
static List<string> _words;
public static string? FindString(Predicate predicate)
{
foreach(var word in _words)
{
if(predicate(word))
{
return word;
}
}
return null;
}
}
public delegate bool Predicate(string str);

public static class WordList
{
static List<string> _words;
public static string? FindString(Predicate predicate)
{
foreach(var word in _words)
{
if(predicate(word))
{
return word;
}
}
return null;
}
}
Now we can easily do
WordList.FindString(str => str == "Hello");
WordList.FindString(str => str.StartsWith("He"));
WordList.FindString(str => str == "Hello");
WordList.FindString(str => str.StartsWith("He"));
This greatly helps with abstraction, instead of having to define every single predicate as a method I forgot to mention, you can use Action and Func as a substitute for declaring your own delegates

Did you find this page helpful?