#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

#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);
        }

#1,136 – Overloading a Generic Method

In the same way that you can overload a generic class, you can overload a generic method, defining multiple generic methods having the same name but different type parameters.  You can also define non-generic methods with the same name.

Below, we overload the Dog.Bury method, defining several non-generic and several generic methods.

    public class Dog
    {
        public string Name { get; set; }

        public Dog(string name)
        {
            Name = name;
        }

        public void Bury(Bone b)
        {
            Console.WriteLine("{0} is burying: {1}", Name, b);
        }

        public void Bury(Lawyer l)
        {
            Console.WriteLine("{0} is burying: {1}", Name, l);
        }

        public void Bury<T>(T thing)
        {
            Console.WriteLine("{0} is burying: {1}", Name, thing);
        }

        public void Bury<T>(T thing, string msg)
        {
            Console.WriteLine("{0} : {1}", msg, thing);
        }

        public void Bury<T1, T2>(T1 thing1, T2 thing2)
        {
            Console.WriteLine("{0} is burying: {1}", Name, thing1);
            Console.WriteLine("{0} is burying: {1}", Name, thing2);
        }
    }

We can call these methods as follows:

            Dog fido = new Dog("Fido");

            fido.Bury(new Bone());
            fido.Bury(new Lawyer());
            fido.Bury<Cow>(new Cow("Bessie"));
            fido.Bury<Lawyer>(new Lawyer(), "One less lawyer");
            fido.Bury<Cow,Cat>(new Cow("Bessie"), new Cat("Puffy"));

#1,038 – Type Parameter Constraints on Generic Methods

You can specify type parameter constraints for both generic types and generic methods.

The example below shows a type constraint for a generic type.

    public class MooPile<T> where T : IMoo
    {
        private List<T> mooPile = new List<T>();

        public void AddMooThing(T thing)
        {
            mooPile.Add(thing);
            mooPile[mooPile.Count - 1].Moo();
        }
    }

And below is a type constraint for a generic method (which happens to be defined within a non-generic class).

        static void SwapAndMoo<T>(ref T item1, ref T item2) where T : IMoo
        {
            T temp = item1;
            item1 = item2;
            item2 = temp;

            item1.Moo();
            item2.Moo();
        }

#816 – Named Argument and Optional Parameter Combinations

You use named arguments (vs. positional) to reverse the order of arguments passed in to a method when you call it.

You use optional parameters (vs. required) to to allow omitting an argument for that parameter when you call the method.

Options include:

  • No named arguments, no optional parameters – you must pass in a value for each parameter, in the proper order
  • Named arguments only – you must pass in an argument for each parameter, but you can change the order of the arguments.  Named arguments must follow positional arguments.
  • Optional parameters only – you must pass arguments in the correct order, but may omit one or more arguments if the corresponding parameter is optional.  If you omit an argument for one parameter, you must omit arguments for all parameters that follow that parameter.
  • Named arguments and optional parameters – you can supply any combination of arguments, in any order

#815 – Named vs. Positional Arguments

C# supports the use of named arguments, in which you can change the order of arguments passed to a function, by prefixing an argument with the corresponding parameter name.

An argument that is not prefixed with the name of a parameter is considered a positional argument.

Positional arguments must come before named arguments and match the order of the corresponding parameters.

        // Sample method with 3 parameters
        public void Bark(int numTimes, string sound, double volume)

Below are some examples of using both named and positional arguments.

            // Example 1: All arguments are positional
            myDog.Bark(2, "Woof", 10.0);

            // Example 2: Only 1st argument is positional
            myDog.Bark(2, volume: 10.0, sound: "Woof");

            // Example 3: All arguments are named
            myDog.Bark(volume: 10.0, sound: "Woof", numTimes: 2);

#814 – Parameters vs. Arguments

A parameter is a variable name for a piece of data passed into or out of a method.    The parameter is the name by which that piece of data is referred to within the body of the method.  Parameters are listed as part of a method’s declaration, within the parentheses that follow the method’s name.

You specify a parameter by indicating the type of the parameter and its name.

        // Bark has 4 parameters
        public void Bark(int numTimes, string sound, ref int globalBarkCount, out bool didBark)

An argument is a constant or variable name passed to a method.  Each argument maps to one of the method’s parameters.

When you specify an argument, you specify the value that will be passed to the method or a variable containing the value.

            // 4 arguments passed to Bark method
            myDog.Bark(2, "Woof", ref globCount, out didBark);

#813 – Defining an Extension Method for an Enumerated Type

In addition to other types, you can extend the functionality of an enumerated type using extension methods.

Below is an extension method that extends the DayOfWeek enumerated type.

        public static string Activity(this DayOfWeek day)
        {
            string activity = "";

            switch (day)
            {
                case DayOfWeek.Sunday:
                    activity = "Reading paper";
                    break;
                case DayOfWeek.Monday:
                    activity = "Grumbling";
                    break;
                case DayOfWeek.Tuesday:
                    activity = "Eating tacos";
                    break;
                case DayOfWeek.Wednesday:
                    activity = "Reading";
                    break;
                case DayOfWeek.Thursday:
                    activity = "Cursing";
                    break;
                case DayOfWeek.Friday:
                    activity = "Celebrating";
                    break;
                case DayOfWeek.Saturday:
                    activity = "Hiking";
                    break;
            }

            return activity;
        }
    }

We can now call the Activity method on any variable or constant whose type is DayOfWeek.

            DayOfWeek today = DayOfWeek.Thursday;
            Console.WriteLine(today.Activity());

            Console.WriteLine(DayOfWeek.Friday.Activity());

813-001