C# Lambdas Part 3, Setting Parameters Up Front, and Mismatching Func and Action Definitions
Over the years, I’ve written a few posts about Actions, Funcs, and how they work with lambdas.
- Simple Action and Action
Examples - Simple Func
and Func<T1, T2, TResult> Examples - C# Lambdas Part 1, a Quick Overview with Examples
- C# Lambdas Part 2, a Few More Complicated Examples
If you are struggling to understand how lambdas, Actions, and Funcs work, I recommend reading those posts first.
This post is going to highlight slightly different use cases for lambdas with Actions and Funcs, it has been a challenge to write, so let me know if you have any suggestions on making it clearer.
But first, the most common use for lambda with Actions and Funcs is to pass in the code that will be executed later. The parameters are not part of the lambda you define.
This is very useful when you want to define the functionality of things like a sort, select, etc., the parameters will naturally come from the collection you are working with.
A very common scenario is filtering a collection -
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var evenNumbersOnlyByLambda = numbers.Where(n => n % 2 == 0)
Another example might be passing a calculation method to be executed on a value that comes from somewhere else. More broadly, you can use them anywhere you want to define functionality and pass that functionality to another piece of code.
Func<int, int, string> AddTwoNumbers = (a, b) => $"{a} + {b} = {a + b}";
Console.WriteLine(Calculation(AddTwoNumbers));
string Calculation(Func<int, int, string> func)
{
//simulate getting the numbers from somewhere else
int num1 = random.Next(1, 10);
int num2 = random.Next(1, 10);
return func(num1, num2);
}
In the posts listed above, you can see many more examples of these common use cases.
Defining Parameters Up Front
But you can also define the parameters up front as part of the lambda.
There are (at least) two ways to do this.
Discarding Lambda Parameters
For something like Func<int, int, string
> you have to have a lambda that passes two parameters and returns a string. But the parameters can be discarded by using (_, _)
.
Func<int, int, string> AddTwoNumbersParamsSetUpFront = (_, _) => $"a + b = {11 + 12}";
Console.WriteLine(Calculation(AddTwoNumbersParamsSetUpFront));
In the above example, the parameters passed to the lambda are discarded, and the lambda uses values defined in its body.
I haven’t used this technique much, but it can be useful when you want to define not only the functionality, but also the parameters that will be used when it is called.
A more general example is -
Func<string, string> funcStringLambda = (_) => "hi";
Console.WriteLine(funcStringLambda());
The Func takes a string as a parameter and returns a string. But the lambda discards the parameter and uses any code to return a string.
Any code, this leads to interesting possibility.
Mismatching the Func Definition and the Method that Satisfies it
A method that takes int
s can be used with the Func<string, string>
. Here’s how.
Func<string, string> funcStringString = (_) => DoWork(5,7);
Console.WriteLine(funcStringString("discarded parameter"));
string DoWork(int a, int b)
{
return $"{a} + {b} = {a + b}";
}
Why does this work? Let’s break up the Func definition.
- The
Func<string, string>
takes a string as a parameter and returns a string. - The lambda
(_)
takes the required parameter, but it is discarded. DoWork
returns a string.
So the lambda takes a string (which is discarded) and returns a string (from DoWork
) - so it matches the Func<string, string>
.
This can lead to some interesting possibilities where you can use a method that takes different parameters than the Func requires, because it is really the lambda that is satisfying the Func definition.
The same principle applies for Actions.
This has been a difficult post to write because the examples are so contrived, but I hope it helps someone.