C# Lambdas Part 2, a Few More Complicated Examples
Download full source code.
This post is a continuation of the previous post on C# lambdas. It is not a good as I would like it to be, but as good as I have time to make it. If you have suggestions, please let me know in the comments.
In this, there are a few more complicated examples, maybe even excessively so. But when I hit code like this in a library I was using it took me a while to understand, so I thought I’d share it here.
The rest of this post is sample code, with detailed comments explaining what each line does.
I strongly encourage you to step through the code line by line to see what is being executed, what parameters are being passed, and what is being returned.
A .NET Fiddle of the code is available here.
I know the comments are a little difficult to read in this blog post, but please download the attached zip file or open the .NET Fiddle.
1using System;
2using System.Linq;
3using System.Net.Http;
4using System.Threading;
5using System.Threading.Tasks;
6using Polly;
7
8public class Program
9{
10 public static async Task Main()
11 {
12 // *** Create cancellationToken and httpClient for examples 1 and 2 *** //
13 var cts = new CancellationTokenSource(3000);
14 var cancellationToken = cts.Token;
15
16 HttpClient httpClient = new HttpClient()
17 {
18 BaseAddress = new System.Uri("https://example.com")
19 };
20
21
22 // *** Example 1 *** //
23 var policy = Policy.HandleResult<HttpResponseMessage>( // HandleResult takes a Func that takes a HttpResponseMessage and returns a bool
24 r => // Passing the HttpResponseMessage
25 !r.IsSuccessStatusCode // Expression lambda returns a bool
26 )
27 .RetryAsync(3, // RetryAsync takes an int for the retryCount
28 onRetry: (response, retryCount) => // and a Action that takes a DelegateResult<HttpResponseMessage> and an int
29 Console.WriteLine($"Got: {response.Result}. Retrying: {retryCount}") // expression lambda prints to the console. Remember this is an Action, nothing is returned
30 );
31
32
33 // *** Example 2 *** //
34 var response = await policy.ExecuteAsync( // ExecuteAsync takes a Func that takes a CancellationToken returns a Task<HttpResponseMessage>
35 (ct) => // CancellationToken
36 httpClient.GetAsync("/", ct), // Expression lambda returns a Task<HttpResponseMessage>, the actual CancellationToken is passed to the Func when the Func is executed by Polly
37 cancellationToken); // The CancellationToken created above, this will be passed to the GetAsync method by Polly
38
39 Console.WriteLine($"Got a response of: {response.StatusCode}");
40
41
42 // *** Example 3 *** //
43 string firstName = "Alan";
44 string runQuery1 =
45 RunQuery( // Call to RunQuery. First parameter to RunQuery is a Func that takes a string parameter and returns a string.
46 (fieldToSearch) => // Start of expression lambda, it takes a string parameter.
47 BuildQuery(fieldToSearch, firstName), // This is the body of the expression lambda. It calls BuildQuery (which returns a string)...
48 // The value of fieldToSearch has not been set yet.
49 "FirstName"); // this corresponds to fieldToSearch in RunQuery, it will be passed to the lambda.
50
51 Console.WriteLine(runQuery1);
52
53
54 // *** Example 4 *** //
55 string lastName = "Adams";
56 string runQuery2 =
57 RunQuery(
58 (fieldToSearch) =>
59 $"SEARCH {fieldToSearch}:{lastName}", // This is the body of the expression lambda. It returns a string.
60 "LastName"); // This corresponds to fieldToSearch in the lambda on the line above.
61
62 Console.WriteLine(runQuery2);
63
64
65 #region Local methods
66 string RunQuery(Func<string, string> func, string fieldToSearch) // fieldToSearch is "FirstName" in Example 3, and "LastName" in Example 4
67 {
68 return $"Running a query - {func(fieldToSearch)}";
69 }
70
71 string BuildQuery(string fieldToSearch, string valueToSearchFor)
72 {
73 return $"SEARCH {fieldToSearch}:{valueToSearchFor}";
74 }
75 #endregion
76
77
78 // *** Example 5 *** //
79 BuildPerson buildPerson = new BuildPerson();
80 var resultFromBuildPersonSimple = buildPerson.BuildPersonSimple( // BuildPersonSimple method takes a Func that takes two string parameters and returns a string.
81 (firstName, lastName) => // The lambda takes two string parameters.
82 $"The person is {firstName} {lastName}", // Expression lambda body takes the two strings and returns a string.
83 "Alan", "Adams" // Two strings are passed to the BuildPersonSimple method
84 );
85 Console.WriteLine(resultFromBuildPersonSimple);
86
87
88 // *** Example 6 *** //
89 var resultFromBuildPersonAdvanced = buildPerson.BuildPersonAdvanced( // BuildPersonSimple method takes a Func that takes two string parameters and an int, and returns a string.
90 (firstName, lastName, age) => // The lambda takes two strings and an int as parameters.
91 $"The person is {firstName} {lastName}, aged {age} years", // Expression lambda body takes the parameters and returns a string.
92 "Betty", "Burns", 30 // Two strings and an int are passed to the BuildPersonAdvanced method
93 );
94 Console.WriteLine(resultFromBuildPersonAdvanced);
95
96
97 // *** Example 7 *** //
98 OtherMethods otherMethods = new OtherMethods();
99 var resultFromOtherMethods = otherMethods.Top( //Top takes a Func that takes two int parameters and returns a string.
100 (a, b) => // The lambda takes two int parameters.
101 $"Result of a + b ({a} + {b}) is {a + b}", // Expression lambda body performs a simple calculation, and returns a string.
102 5 // Passed to the Top method as num1
103 );
104
105 Console.WriteLine(resultFromOtherMethods);
106 }
107}
108
109public class OtherMethods
110{
111 public string Top(Func<int, int, string> func, int num1)
112 => Bottom((x, y) => func(x, y), num1); // Top method does nothing except call Bottom, passing the func and num1(5).
113
114 public string Bottom(Func<int, int, string> func, int num1)
115 {
116 int localNumber = 10;
117 return func(num1, localNumber); // num1 and localNumber are passed to the lambda.
118 }
119}
120public class BuildPerson
121{
122 public string BuildPersonSimple(Func<string, string, string> buildPersonSimpleFunc, string firstName, string lastName)
123 => BuildPersonAdvanced( // Call to BuildPersonAdvanced method, BuildPersonAdvanced takes a Func that takes a two strings an an int as parameters and returns a string.
124 (firstName, lastName, _) => // Because the Func parameter to BuildPersonAdvanced takes two strings and an int, I have to pass in three parameters, but I discarded the int because I don't have an age to pass.
125 buildPersonSimpleFunc(firstName, lastName), // The Func in BuildPersonAdvanced must return a string, buildPersonSimpleFunc returns a string, so it can be used here.
126 firstName, lastName, default(int)); // Pass on the two strings. But not passing anything for age, so using default(int).
127
128
129 public string BuildPersonAdvanced(Func<string, string, int, string> buildPersonAdvancedFunc, string firstName, string lastName, int age)
130 {
131 // This is where things get more complicated.
132 // Look at buildPersonAdvancedFunc it takes two strings, and an int, and returns a string.
133 // But you can see from the BuildPersonSimple Method, that the buildPersonSimpleFunc that was passed to BuildPersonAdvanced as buildPersonAdvancedFunc, but buildPersonSimpleFunc only takes two strings, and returns a string. It doesn't take an int.
134 // Now to invoke buildPersonAdvancedFunc, you must pass in two strings, and an int.
135 // For example 5, the age will not be used, but for example 6 it will be.
136
137 return $"{buildPersonAdvancedFunc(firstName, lastName, age)}";
138 }
139}
Download full source code.