Lambda Expressions

Delegates

So what is a lambda (λ) expression? To fully understand this one must first have a firm understanding of delegates. In C# the reference type of delegate is used to define a named or an anonymous method signature.  More on delegates here.

A delegate is simply a means of defining a return type (including void) and corresponding list of parameters (or none at all). Consider the following:

    delegate void OnComplete();

    delegate void OnValidation(bool isValid);

    delegate decimal Multiply(decimal leftOperand, decimal rightOperand);

    delegate int RequestCount();

From the four delegates outlined above, we can infer usage based solely on the return and parameters types, using the name as a further hint. All four of these examples are valid, noting that some return void and some expect parameters while other do not. Most void return delegates will be associated to events, whereas delegates that return something other than void are not event based — however this is not always true.

The .NET framework has provided some public delegates for simplifying generic usage. The Action delegate always are a return type of void as does Action<in T, …> delegates, but these accept any combination of parameters (with an upper bounds of 16). The Func<out TResult> delegate returns the type of TResult as does Func<in T, …, out TResult>, but these accept any combination of parameters (with an upper bounds of 16).

Example Class

Imagine having a parser class (for simplicity) that encapsulates the common struct TryParse functionality.

    static class Parse
    {
        delegate bool ParseDelegate<T>(string s, out T result) where T : struct;
    }

Now the internal/static parser class has a delegate that returns either true or false, and accepts a string value and then ‘out’ the result of T. We can now easily add to this static method entry points that utilize our delegate, and encapsulate the core functionality.

    static class Parse
    {
        delegate bool ParseDelegate<T>(string s, out T result) where T : struct;

        static T TryParse<T>(string value, ParseDelegate<T> parse) where T : struct
        {
            T result;
            parse(value as string, out result);
            return result;
        }

        public static int ToInt32(string value)
        {
            return TryParse<int>(value, int.TryParse);
        }
    }

Note: there is a constraint on the type of T, it’s enforced to only ever be a value type “struct”. This is useful since the majority of TryParse functions that exist today in the .NET framework are implemented from structs, i.e.; DateTime, TimeSpan, IPAddress, byte, short, int, long, float, double, decimal, etc… In other words, all of these types have a TryParse method on them that match our delegate. Below is a simple test that will show that with a single line we can easily replicate the what was done in 9 lines with manually invoking the int.TryParse for each value in our string array namely “tests”.

        static void Main(string[] args)
        {
            var tests = new[] { "1", "Something that is not a number.", string.Empty, null };

            // Iterate each value and invoke int.TryParse manually.
            foreach (var test in tests)
            {
                int result;
                if (int.TryParse(test, out result))
                {
                    Console.WriteLine(result);
                }
                else
                {
                    Console.WriteLine(result);
                }
            }

            // Iterate each value and invoke Parse.ToInt32.
            foreach (var test in tests)
            {
                Console.WriteLine(Parse.ToInt32(test));
            }
            
            Console.ReadKey();
        }

        // Output
        // 1
        // 0
        // 0
        // 0
        // 1
        // 0
        // 0
        // 0

Disclaimer: while this code serves the purpose demonstrating delegates — it is not intended for everyday use, there are more often than not places in code where knowing whether or not the value being parsed was actually parsed. This parser class hides from the consumer whether or not said value was parsed, returning the default(T) if unable to parse it.

Moving On

A lambda expression is simply a means of defining a delegate anonymously, but with its implementation logic driving it. LINQ is one of the most common chaining pattern implementations in the .NET framework today, not to mention its countless extensions on the most extended interface IEnumerable<T> (because who doesn’t have love for yield and deferred execution). LINQ is a great basis for demonstrating lambdas. Consider the following (using our example parser class from earlier):

        static void Main(string[] args)
        {
            var tests = new[] { "1", "Something that is not a number.", string.Empty, null };

            foreach (var test in tests.Where(IsNotDefaultInt32))
            {
                Console.WriteLine(test);
            }

            Console.ReadKey();
        }

        static bool IsNotDefaultInt32(string value)
        {
            return Parser.ToInt32(value) != default(int);
        }

        // Output
        // 1

With LINQ, I can now call ‘Where’ on the “tests” array as it implements the IEnumerable<T> interface where T is string. The Where extension method expects a predicate (not to be confused with public delegate Predicate). The predicate is a Func<T, bool> where T is the string from the array iteration, and the returning of the bool is to let the Where method know which ones to return.

This doesn’t actually have a lambda expression, but rather a named method that fulfills the delegates signature requirements. We could instead use a lambda expression as follows:

        static void Main(string[] args)
        {
            var tests = new[] { "1", "Something that is not a number.", string.Empty, null };

            foreach (var test in tests.Where(value => Parser.ToInt32(value) != default(int)))
            {
                Console.WriteLine(test);
            }

            Console.ReadKey();
        }

        // Output
        // 1

Notice that there is no longer a need for the named method. We instead have the Where method’s argument populated with an expression.

        value => Parser.ToInt32(value) != default(int)

This expression is read as, the “string” value goes to the evaluation of the statement expression, in other words for this value where the value is parsed and not the default int value.