#1,173 – Lambda Expression Can Be Just an Expression

You’ll often see lambda expressions written as either a single statement (no return value) or a block of statements (optional return value).

        static void SomeMethod(int i, string s)
        {
            // do something with int and string
        }

        static void Main(string[] args)
        {
            // Single statement
            Action<int, string> thing1 = (i, s) => SomeMethod(i, s);

            // Block of statements, no return value
            Action<int, string> thing2 = (i, s) =>
            {
                for (int i2 = i; i2 <= i + 10; i2++)
                    SomeMethod(i2, s);
            };

            // Block of statements with return value
            Func<int,int> thing3 = (i) =>
            {
                SomeMethod(i, "x");
                return i + 1;
            };
        }

A lambda expression can also be a single statement representing an expression whose type is assignment compatible with the return value of the delegate type being assigned to.

            // Expression
            Func<int, int> doubleMe = (i) => 2 * i;
            Console.WriteLine(doubleMe(42));

1173-001

#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,170 – You Can’t Unsubscribe from an Event Using a Lambda Expression

Suppose that you subscribe to an event using a lambda expression:

            Dog d = new Dog("Bowser");

            // NOTE: Barked is EventHandler<string>
            d.Barked += (s, e) => Console.WriteLine("Bark: {0}", e);

You cannot, however, unsubscribe using the same syntax. The -= operator shown below will be using a different anonymous method, so the original will not be removed from the event’s invocation list.

            // Not what you expect
            d.Barked -= (s, e) => Console.WriteLine("Bark: {0}", e); 

So the lambda expression syntax is fine–as long as you don’t need to unsubscribe from the event.  (More on that in a future post).

If you want to unsubscribe, but still use lambda syntax, you could persist the delegate instance.

            EventHandler<string> handler = (s, e) => Console.WriteLine("Bark: {0}", e);
            d.Barked += handler;

            // ...

            d.Barked -= handler;

#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,168 – Using a Lambda Expression as an Event Handler

You can use a lambda expression wherever a delegate instance is expected.  This includes using a lambda expression when defining an event handler.

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

            // Method #1 - Subscribe to Barked event using named method
            d.Barked += d_Barked;

            // Method #2 - Subscribe to Barked event using 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("Dog {0} just barked, saying {1}",
                ((Dog)sender).Name, e);
        }

1168-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

Follow

Get every new post delivered to your Inbox.

Join 440 other followers