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.

comments powered by Disqus

Related