#1,186 – Capturing a foreach Iteration Variable in a Lambda Expression

If you capture a for loop iteration variable in a lambda expression, the value of the variable that the expression uses will be whatever the final value of the iteration variable is when the loop completes.

Capturing the iteration variable in a foreach loop works differently.  When you capture the iteration variable in a foreach loop, the lambda expression has a copy of the iteration variable with the value that it had at the time that it was captured.

            int[] primes = { 2, 3, 5, 7, 11, 13 };
            List<Action> dels = new List<Action>();

            // Capture foreach iteration variable
            foreach (int p in primes)
                dels.Add(() => Console.WriteLine(p));

            foreach (Action a in dels)
                a();

1186-001

Advertisement

#1,172 – Lambda Expressions Often Used with Func and Action

Lambda expressions are quite often used with the Func and Action delegate types.  Func<> accepts zero or more parameters and returns a result.  Action<> accepts zero or more parameters and returns void.

Below are examples of lambda expressions using these delegate types.

            // Function: no parameters, returns int
            Func<int> dayOfYear = () => DateTime.Now.DayOfYear;
            int doy = dayOfYear();

            // Function: 2 parameters, returns string
            Func<int, double, string> intDoubleAdder =
                (int i, double d) =>
                {
                    double result = i + d;
                    return string.Format("Result is {0}", result);
                };
            string answer = intDoubleAdder(5, 4.2);

            // Action: no parameters
            Action todayReporter = () =>
                Console.WriteLine(string.Format("Today is {0}", DateTime.Today.ToLongDateString()));
            todayReporter();

            // Action: 3 parameters
            Action<string,int,double> goofyDel =
                (s, i, d) => Console.WriteLine(string.Format(s,i,d));
            goofyDel("Int is {0}, Double is {1}", 5, 12.2);

#1,171 – Lambda Expression Internals

Suppose that we have the following lambda expression (doubles a number) and that we invoke it.

    class Program
    {
        static void Main(string[] args)
        {
            Func<int, int> intDoubler = x => 2 * x;
            Console.WriteLine(intDoubler(12));
        }
    }

1171-001
We can compile this code and then use the IL Disassembler to inspect the resulting IL.

We can see that in our main class we have a static Main method and also a private static method defined by the compiler to contain our lambda expression.  In this case, it’s named <Main>b__0.

1171-002

Cracking open Main() reveals that it indeed calls <Main>b__0.

1171-003

And looking at the body of <Main>b__0, we can see that it indeed just doubles the input parameter.

1171-004

#1,169 – Lambdas Supersede Anonymous Methods

Lambda expressions were introduced in C# 3.0 and supersede anonymous methods, which were introduced in C# 2.0.  There are some differences between lambda expressions and anonymous methods, but lambdas are now the preferred way to write inline code.

        static void Main(string[] args)
        {
            Dog d = new Dog("Bowser");

            // C# 1.0 - Named method
            d.Barked += d_Barked;

            // C# 2.0 - Anonymous method
            d.Barked += delegate (object sender,string e) {
                Console.WriteLine("My dog says {0}", e); };

            // C# 3.0 - Lambda expression
            d.Barked += (s, e) => Console.WriteLine("My dog says {0}", e); 

            d.Bark();

            Console.ReadLine();
        }

        static void d_Barked(object sender, string e)
        {
            Console.WriteLine("My dog says {0}", e);
        }

1169-001

#1,167 – Passing a Lambda Expression to a Method

You can use a lambda expression anywhere that a delegate instance is expected.  This includes passing a lambda expression as a parameter to a method that takes a delegate type.

In the example below, we have a method that has two parameters that are delegate types.  It takes a transformer function that accepts and int and returns an int.  It also accepts a delegate that reports something about two different int values.

            // Transform one int to another int and report the result
            static void TransformAndReport(Func<int,int> transformer, Action<int,int> reporter, int[] data)
            {
                foreach (int ival in data)
                {
                    int newVal = transformer(ival);
                    reporter(ival, newVal);
                }
            }

We can call this method by passing in two different lambda expressions.

            TransformAndReport(
                (i) => i * 3,
                (inVal,outVal) => Console.WriteLine("{0} became {1}", inVal, outVal),
                new int[]{1, 2, 3});

            TransformAndReport(
                (i) => {
                    double cosVal = Math.Cos(i);
                    return (int)Math.Truncate((cosVal * 100) - i);
                },
                (inVal, outVal) => Console.WriteLine("{0} yields {1}", inVal, outVal),
                new int[] { 20, 40, 60, 90 });

1167-001

#1,166 – Lambda Expression Syntax

A lambda expression is composed of:

  • A parameter list with variable names, representing 0 or more parameters
  • A lambda operator:  =>  (read as “goes to”)
  • One of
    • Single statement (no return value)
    • Block of statements with optional return statement
    • Single statement that is an expression

Below are examples of the different variants.

            // No input params
            Action del1 = () => Console.WriteLine("Hi");
            del1();

            // Single input parameter
            Action<int> del2 = i => Console.WriteLine(i);
            del2(5);

            // Multiple input parameters
            Action<int, string> del3 = (i, s) => Console.WriteLine(i.ToString() + s);
            del3(5, "Bob");

            // Input integer, return string
            Func<int, string> del4 = (i) => string.Format("Double: {0}", i * 2);
            string s2 = del4(5);
            Console.WriteLine(s2);

            // Multiple statements in body
            Action<int> del5 =
                (i) => {
                    Console.WriteLine(i);
                    double dCubed = Math.Pow(i, 3.0);
                    Console.WriteLine(dCubed);
                };
            del5(5);

            // Input parameter, return value, statement block
            Func<int, int> del6 =
                (i) =>
                {
                    int i2 = i * 10 + 12;
                    return i2;
                };
            del6(42);

1166-001

#1,165 – Lambda Expression Basics

A lambda expression is an unnamed method that appears in-line in the code where it used.  It can be used wherever your code expects an instance of a delegate.

Below, the lambda expression takes the place of a method name in assigning a value to a delegate instance.  The in-line expression behaves exactly as the method does, returning a string constructed from two input parameters (int and double).

In either case, we can invoke the code (named method or lambda expression) by invoking the delegate instance and passing it the input parameters.

        delegate string IntDoubleDelegate(int i, double d);

        static void Main(string[] args)
        {
            // Method #1 - set delegate instance
            // to named method.
            IntDoubleDelegate del1 = IntDoubleInfo;
            string info1 = del1(5, 12.3);

            // Method #2 - use lambda expression
            IntDoubleDelegate del2 = (i, d) => string.Format("I:{0}, D:{1}", i, d);
            string info2 = del2(5, 12.3);
        }

        static string IntDoubleInfo(int i, double d)
        {
            return string.Format("I:{0}, D:{1}", i, d);
        }