Delegates, Events, and Lambda Expressions - Advanced C# Programming - C# 6.0 and the .NET 4.6 Framework (2015)

C# 6.0 and the .NET 4.6 Framework (2015)

PART IV

image

Advanced C# Programming

CHAPTER 10

image

Delegates, Events, and Lambda Expressions

Up to this point in the text, most of the applications you developed added various bits of code to Main(), which, in some way or another, sent requests to a given object. However, many applications require that an object be able to communicate back to the entity that created it using a callback mechanism. While callback mechanisms can be used in any application, they are especially critical for graphical user interfaces in that controls (such as a button) need to invoke external methods under the correct circumstances (when the button is clicked, when the mouse enters the button surface, and so forth).

Under the .NET platform, the delegate type is the preferred means of defining and responding to callbacks within applications. Essentially, the .NET delegate type is a type-safe object that “points to” a method or a list of methods that can be invoked at a later time. Unlike a traditional C++ function pointer, however, .NET delegates are classes that have built-in support for multicasting and asynchronous method invocation.

In this chapter, you will learn how to create and manipulate delegate types, and then you’ll investigate the C# event keyword, which streamlines the process of working with delegate types. Along the way, you will also examine several delegate- and event-centric language features of C#, including anonymous methods and method group conversions.

I wrap up this chapter by examining lambda expressions. Using the C# lambda operator (=>), you can specify a block of code statements (and the parameters to pass to those code statements) wherever a strongly typed delegate is required. As you will see, a lambda expression is little more than an anonymous method in disguise and provides a simplified approach to working with delegates. In addition, this same operation (as of .NET 4.6) can be used to implement a single-statement method or property using a concise syntax.

Understanding the .NET Delegate Type

Before formally defining .NET delegates, let’s gain a bit of perspective. Historically, the Windows API made frequent use of C-style function pointers to create entities termed callback functions, or simply callbacks. Using callbacks, programmers were able to configure one function to report back to (call back) another function in the application. With this approach, Windows developers were able to handle button clicking, mouse moving, menu selecting, and general bidirectional communications between two entities in memory.

In the .NET Framework, callbacks are accomplished in a type-safe and object-oriented manner using delegates. In essence, a delegate is a type-safe object that points to another method (or possibly a list of methods) in the application, which can be invoked at a later time. Specifically, a delegate maintains three important pieces of information.

· The address of the method on which it makes calls

· The parameters (if any) of this method

· The return type (if any) of this method

Image Note .NET delegates can point to either static or instance methods.

After a delegate object has been created and given the necessary information, it may dynamically invoke the method(s) it points to at runtime. Every delegate in the .NET Framework (including your custom delegates) is automatically endowed with the ability to call its methodssynchronously or asynchronously. This fact greatly simplifies programming tasks, given that you can call a method on a secondary thread of execution without manually creating and managing a Thread object.

Image Note You will examine the asynchronous behavior of delegate types during your investigation of threading and asynchronous calls in Chapter 19. In this chapter, you are concerned only with the synchronous aspects of the delegate type.

Defining a Delegate Type in C#

When you want to create a delegate type in C#, you use the delegate keyword. The name of your delegate type can be whatever you desire. However, you must define the delegate to match the signature of the method(s) it will point to. For example, the following delegate type (namedBinaryOp) can point to any method that returns an integer and takes two integers as input parameters (you will build and use this delegate yourself a bit later in this chapter, so hang tight for now):

// This delegate can point to any method,
// taking two integers and returning an integer.
public delegate int BinaryOp(int x, int y);

When the C# compiler processes delegate types, it automatically generates a sealed class deriving from System.MulticastDelegate. This class (in conjunction with its base class, System.Delegate) provides the necessary infrastructure for the delegate to hold onto a list of methods to be invoked at a later time. For example, if you were to examine the BinaryOp delegate using ildasm.exe, you would find the class shown in Figure 10-1 (you will build this full example in just a moment if you want to check for yourself).

image

Figure 10-1. The C# delegate keyword represents a sealed class deriving from System.MulticastDelegate

As you can see, the compiler-generated BinaryOp class defines three public methods. Invoke() is perhaps the key method, as it is used to invoke each method maintained by the delegate object in a synchronous manner, meaning the caller must wait for the call to complete before continuing on its way. Strangely enough, the synchronous Invoke() method may not need to be called explicitly from your C# code. As you will see in just a bit, Invoke() is called behind the scenes when you use the appropriate C# syntax.

BeginInvoke() and EndInvoke() provide the ability to call the current method asynchronously on a separate thread of execution. If you have a background in multithreading, you know that one of the most common reasons developers create secondary threads of execution is to invoke methods that require time to complete. Although the .NET base class libraries supply several namespaces devoted to multithreaded and parallel programming, delegates provide this functionality out of the box.

Now, how exactly does the compiler know how to define the Invoke(), BeginInvoke(), and EndInvoke() methods? To understand the process, here is the crux of the compiler-generated BinaryOp class type (bold italic marks the items specified by the defined delegate type):

sealed class BinaryOp : System.MulticastDelegate
{
public int Invoke(int x, int y);
public IAsyncResult BeginInvoke(int x, int y,
AsyncCallback cb, object state);
public int EndInvoke(IAsyncResult result);
}

First, notice that the parameters and return type defined for the Invoke() method exactly match the definition of the BinaryOp delegate. The initial parameters to BeginInvoke() members (two integers, in this case) are also based on the BinaryOp delegate; however,BeginInvoke() will always provide two final parameters (of type AsyncCallback and object) that are used to facilitate asynchronous method invocations. Finally, the return type of EndInvoke() is identical to the original delegate declaration and will always take as a sole parameter an object implementing the IAsyncResult interface.

Let’s see another example. Assume you have defined a delegate type that can point to any method returning a string and receiving three System.Boolean input parameters.

public delegate string MyDelegate(bool a, bool b, bool c);

This time, the compiler-generated class breaks down as follows:

sealed class MyDelegate : System.MulticastDelegate
{
public string Invoke(bool a, bool b, bool c);
public IAsyncResult BeginInvoke(bool a, bool b, bool c,
AsyncCallback cb, object state);
public string EndInvoke(IAsyncResult result);
}

Delegates can also “point to” methods that contain any number of out or ref parameters (as well as array parameters marked with the params keyword). For example, assume the following delegate type:

public delegate string MyOtherDelegate(out bool a, ref bool b, int c);

The signatures of the Invoke() and BeginInvoke() methods look as you would expect; however, check out the following EndInvoke() method, which now includes the set of all out/ref arguments defined by the delegate type:

public sealed class MyOtherDelegate : System.MulticastDelegate
{
public string Invoke(out bool a, ref bool b, int c);
public IAsyncResult BeginInvoke(out bool a, ref bool b, int c,
AsyncCallback cb, object state);
public string EndInvoke(out bool a, ref bool b, IAsyncResult result);
}

To summarize, a C# delegate type definition results in a sealed class with three compiler-generated methods whose parameter and return types are based on the delegate’s declaration. The following pseudocode approximates the basic pattern:

// This is only pseudo-code!
public sealed class DelegateName : System.MulticastDelegate
{
public delegateReturnValue Invoke(allDelegateInputRefAndOutParams);

public IAsyncResult BeginInvoke(allDelegateInputRefAndOutParams,
AsyncCallback cb, object state);

public delegateReturnValue EndInvoke(allDelegateRefAndOutParams,
IAsyncResult result);
}

The System.MulticastDelegate and System.Delegate Base Classes

So, when you build a type using the C# delegate keyword, you are indirectly declaring a class type that derives from System.MulticastDelegate. This class provides descendants with access to a list that contains the addresses of the methods maintained by the delegate object, as well as several additional methods (and a few overloaded operators) to interact with the invocation list. Here are some select members of System.MulticastDelegate:

public abstract class MulticastDelegate : Delegate
{
// Returns the list of methods "pointed to."
public sealed override Delegate[] GetInvocationList();

// Overloaded operators.
public static bool operator ==(MulticastDelegate d1, MulticastDelegate d2);
public static bool operator !=(MulticastDelegate d1, MulticastDelegate d2);

// Used internally to manage the list of methods maintained by the delegate.
private IntPtr _invocationCount;
private object _invocationList;
}

System.MulticastDelegate obtains additional functionality from its parent class, System.Delegate. Here is a partial snapshot of the class definition:

public abstract class Delegate : ICloneable, ISerializable
{
// Methods to interact with the list of functions.
public static Delegate Combine(params Delegate[] delegates);
public static Delegate Combine(Delegate a, Delegate b);
public static Delegate Remove(Delegate source, Delegate value);
public static Delegate RemoveAll(Delegate source, Delegate value);

// Overloaded operators.
public static bool operator ==(Delegate d1, Delegate d2);
public static bool operator !=(Delegate d1, Delegate d2);

// Properties that expose the delegate target.
public MethodInfo Method { get; }
public object Target { get; }
}

Now, understand that you can never directly derive from these base classes in your code (it is a compiler error to do so). Nevertheless, when you use the delegate keyword, you have indirectly created a class that “is-a” MulticastDelegate. Table 10-1 documents the core members common to all delegate types.

Table 10-1. Select Members of System.MultcastDelegate/System.Delegate

Member

Meaning in Life

Method

This property returns a System.Reflection.MethodInfo object that represents details of a static method maintained by the delegate.

Target

If the method to be called is defined at the object level (rather than a static method), Target returns an object that represents the method maintained by the delegate. If the value returned from Target equals null, the method to be called is a static member.

Combine()

This static method adds a method to the list maintained by the delegate. In C#, you trigger this method using the overloaded += operator as a shorthand notation.

GetInvocationList()

This method returns an array of System.Delegate objects, each representing a particular method that may be invoked.

Remove() RemoveAll()

These static methods remove a method (or all methods) from the delegate’s invocation list. In C#, the Remove() method can be called indirectly using the overloaded -= operator.

The Simplest Possible Delegate Example

To be sure, delegates can cause some confusion when encountered for the first time. Thus, to get the ball rolling, let’s take a look at a simple Console Application program (named SimpleDelegate) that makes use of the BinaryOp delegate type you’ve seen previously. Here is the complete code, with analysis to follow:

namespace SimpleDelegate
{
// This delegate can point to any method,
// taking two integers and returning an integer.
public delegate int BinaryOp(int x, int y);

// This class contains methods BinaryOp will
// point to.
public class SimpleMath
{
public static int Add(int x, int y)
{ return x + y; }
public static int Subtract(int x, int y)
{ return x - y; }
}

class Program
{
static void Main(string[] args)
{
Console.WriteLine("***** Simple Delegate Example *****\n");

// Create a BinaryOp delegate object that
// "points to" SimpleMath.Add().
BinaryOp b = new BinaryOp(SimpleMath.Add);

// Invoke Add() method indirectly using delegate object.
Console.WriteLine("10 + 10 is {0}", b(10, 10));
Console.ReadLine();
}
}
}

Again, notice the format of the BinaryOp delegate type declaration; it specifies that BinaryOp delegate objects can point to any method taking two integers and returning an integer (the actual name of the method pointed to is irrelevant). Here, you have created a class namedSimpleMath, which defines two static methods that match the pattern defined by the BinaryOp delegate.

When you want to assign the target method to a given delegate object, simply pass in the name of the method to the delegate’s constructor.

// Create a BinaryOp delegate object that
// "points to" SimpleMath.Add().
BinaryOp b = new BinaryOp(SimpleMath.Add);

At this point, you are able to invoke the member pointed to using a syntax that looks like a direct function invocation.

// Invoke() is really called here!
Console.WriteLine("10 + 10 is {0}", b(10, 10));

Under the hood, the runtime actually calls the compiler-generated Invoke() method on your MulticastDelegate derived class. You can verify this for yourself if you open your assembly in ildasm.exe and examine the CIL code within the Main() method .

.method private hidebysig static void Main(string[] args) cil managed
{
...
callvirt instance int32 SimpleDelegate.BinaryOp::Invoke(int32, int32)
}

C# does not require you to explicitly call Invoke() within your code base. Because BinaryOp can point to methods that take two arguments, the following code statement is also permissible:

Console.WriteLine("10 + 10 is {0}", b.Invoke(10, 10));

Recall that .NET delegates are type safe. Therefore, if you attempt to create a delegate object pointing to a method that does not match the pattern, you receive a compile-time error. To illustrate, assume the SimpleMath class now defines an additional method namedSquareNumber() , which takes a single integer as input.

public class SimpleMath
{
...
public static int SquareNumber(int a)
{ return a * a; }
}

Given that the BinaryOp delegate can point only to methods that take two integers and return an integer, the following code is illegal and will not compile:

// Compiler error! Method does not match delegate pattern!
BinaryOp b2 = new BinaryOp(SimpleMath.SquareNumber);

Investigating a Delegate Object

Let’s spice up the current example by creating a static method (named DisplayDelegateInfo() ) within the Program class. This method will print out the names of the methods maintained by a delegate object, as well as the name of the class defining the method. To do this, you will iterate over the System.Delegate array returned by GetInvocationList(), invoking each object’s Target and Method properties.

static void DisplayDelegateInfo(Delegate delObj)
{
// Print the names of each member in the
// delegate’s invocation list.
foreach (Delegate d in delObj.GetInvocationList())
{
Console.WriteLine("Method Name: {0}", d.Method);
Console.WriteLine("Type Name: {0}", d.Target);
}
}

Assuming you have updated your Main() method to actually call this new helper method, as shown here:

BinaryOp b = new BinaryOp(SimpleMath.Add);
DisplayDelegateInfo(b);

you would find the output shown next:

***** Simple Delegate Example *****

Method Name: Int32 Add(Int32, Int32)
Type Name:
10 + 10 is 20

Notice that the name of the target class (SimpleMath) is currently not displayed when calling the Target property. The reason has to do with the fact that your BinaryOp delegate is pointing to a static method and, therefore, there is no object to reference! However, if you update theAdd() and Subtract() methods to be nonstatic (simply by deleting the static keywords), you could create an instance of the SimpleMath class and specify the methods to invoke using the object reference.

static void Main(string[] args)
{
Console.WriteLine("***** Simple Delegate Example *****\n");

// .NET delegates can also point to instance methods as well.
SimpleMath m = new SimpleMath();
BinaryOp b = new BinaryOp(m.Add);

// Show information about this object.
DisplayDelegateInfo(b);

Console.WriteLine("10 + 10 is {0}", b(10, 10));
Console.ReadLine();
}

In this case, you would find the output shown here:

***** Simple Delegate Example *****

Method Name: Int32 Add(Int32, Int32)
Type Name: SimpleDelegate.SimpleMath
10 + 10 is 20

Image Note The SimpleDelegate project is located in the Chapter 10 subdirectory.

Sending Object State Notifications Using Delegates

Clearly, the previous SimpleDelegate example was intended to be purely illustrative in nature, given that there would be no compelling reason to define a delegate simply to add two numbers. To provide a more realistic use of delegate types, let’s use delegates to define a Car class that has the ability to inform external entities about its current engine state. To do so, you will take the following steps:

1. Define a new delegate type that will be used to send notifications to the caller.

2. Declare a member variable of this delegate in the Car class.

3. Create a helper function on the Car that allows the caller to specify the method to call back on.

4. Implement the Accelerate() method to invoke the delegate’s invocation list under the correct circumstances.

To begin, create a new Console Application project named CarDelegate. Now, define a new Car class that looks initially like this:

public class Car
{
// Internal state data.
public int CurrentSpeed { get; set; }
public int MaxSpeed { get; set; } = 100;
public string PetName { get; set; }

// Is the car alive or dead?
private bool carIsDead;

// Class constructors.
public Car() {}
public Car(string name, int maxSp, int currSp)
{
CurrentSpeed = currSp;
MaxSpeed = maxSp;
PetName = name;
}
}

Now, consider the following updates, which address the first three points:

public class Car
{
...
// 1) Define a delegate type.
public delegate void CarEngineHandler(string msgForCaller);

// 2) Define a member variable of this delegate.
private CarEngineHandler listOfHandlers;

// 3) Add registration function for the caller.
public void RegisterWithCarEngine(CarEngineHandler methodToCall)
{
listOfHandlers = methodToCall;
}
}

Notice in this example that you define the delegate types directly within the scope of the Car class, which is certainly not necessary but does help enforce the idea that the delegate works naturally with this particular class. The delegate type, CarEngineHandler, can point to any method taking a single string as input and void as a return value.

Next, note that you declare a private member variable of your delegate type (named listOfHandlers) and a helper function (named RegisterWithCarEngine() ) that allows the caller to assign a method to the delegate’s invocation list.

Image Note Strictly speaking, you could have defined your delegate member variable as public, therefore avoiding the need to create additional registration methods. However, by defining the delegate member variable as private, you are enforcing encapsulation services and providing a more type-safe solution. You’ll revisit the risk of public delegate member variables later in this chapter when you look at the C# event keyword.

At this point, you need to create the Accelerate() method . Recall, the point here is to allow a Car object to send engine-related messages to any subscribed listener. Here is the update:

// 4) Implement the Accelerate() method to invoke the delegate’s
// invocation list under the correct circumstances.
public void Accelerate(int delta)
{
// If this car is "dead," send dead message.
if (carIsDead)
{
if (listOfHandlers != null)
listOfHandlers("Sorry, this car is dead...");
}
else
{
CurrentSpeed += delta;

// Is this car "almost dead"?
if (10 == (MaxSpeed - CurrentSpeed)
&& listOfHandlers != null)
{
listOfHandlers("Careful buddy! Gonna blow!");
}
if (CurrentSpeed >= MaxSpeed)
carIsDead = true;
else
Console.WriteLine("CurrentSpeed = {0}", CurrentSpeed);
}
}

Notice that before you invoke the methods maintained by the listOfHandlers member variable, you are checking it against a null value. The reason is that it will be the job of the caller to allocate these objects by calling the RegisterWithCarEngine() helper method . If the caller does not call this method and you attempt to invoke the delegate’s invocation list, you will trigger a NullReferenceException at runtime. Now that you have the delegate infrastructure in place, observe the updates to the Program class:

class Program
{
static void Main(string[] args)
{
Console.WriteLine("***** Delegates as event enablers *****\n");

// First, make a Car object.
Car c1 = new Car("SlugBug", 100, 10);

// Now, tell the car which method to call
// when it wants to send us messages.
c1.RegisterWithCarEngine(new Car.CarEngineHandler(OnCarEngineEvent));

// Speed up (this will trigger the events).
Console.WriteLine("***** Speeding up *****");
for (int i = 0; i < 6; i++)
c1.Accelerate(20);
Console.ReadLine();
}

// This is the target for incoming events.
public static void OnCarEngineEvent(string msg)
{
Console.WriteLine("\n***** Message From Car Object *****");
Console.WriteLine("=> {0}", msg);
Console.WriteLine("***********************************\n");
}
}

The Main() method begins by simply making a new Car object. Since you are interested in hearing about the engine events, the next step is to call your custom registration function, RegisterWithCarEngine(). Recall that this method expects to be passed an instance of the nested CarEngineHandler delegate, and as with any delegate, you specify a “method to point to” as a constructor parameter. The trick in this example is that the method in question is located back in the Program class! Again, notice that the OnCarEngineEvent() method is a dead-on match to the related delegate in that it takes a string as input and returns void. Consider the output of the current example:

***** Delegates as event enablers *****
***** Speeding up *****
CurrentSpeed = 30
CurrentSpeed = 50
CurrentSpeed = 70

***** Message From Car Object *****
=> Careful buddy! Gonna blow!
***********************************
CurrentSpeed = 90
***** Message From Car Object *****
=> Sorry, this car is dead...
***********************************

Enabling Multicasting

Recall that .NET delegates have the built-in ability to multicast. In other words, a delegate object can maintain a list of methods to call, rather than just a single method. When you want to add multiple methods to a delegate object, you simply use the overloaded += operator, rather than a direct assignment. To enable multicasting on the Car class, you could update the RegisterWithCarEngine()method, like so:

public class Car
{
// Now with multicasting support!
// Note we are now using the += operator, not
// the assignment operator (=).
public void RegisterWithCarEngine(CarEngineHandler methodToCall)
{
listOfHandlers += methodToCall;
}
...
}

When you use the += operator on a delegate object, the compiler resolves this to a call on the static Delegate.Combine() method. In fact, you could call Delegate.Combine() directly; however, the += operator offers a simpler alternative. There is no need to modify your current RegisterWithCarEngine() method, but here is an example if using Delegate.Combine() rather than the += operator:

public void RegisterWithCarEngine( CarEngineHandler methodToCall )
{
if (listOfHandlers == null)
listOfHandlers = methodToCall;
else
Delegate.Combine(listOfHandlers, methodToCall);
}

In any case, the caller can now register multiple targets for the same callback notification. Here, the second handler prints the incoming message in uppercase, just for display purposes:

class Program
{
static void Main(string[] args)
{
Console.WriteLine("***** Delegates as event enablers *****\n");

// First, make a Car object.
Car c1 = new Car("SlugBug", 100, 10);

// Register multiple targets for the notifications.
c1.RegisterWithCarEngine(new Car.CarEngineHandler(OnCarEngineEvent));
c1.RegisterWithCarEngine(new Car.CarEngineHandler(OnCarEngineEvent2));

// Speed up (this will trigger the events).
Console.WriteLine("***** Speeding up *****");
for (int i = 0; i < 6; i++)
c1.Accelerate(20);
Console.ReadLine();
}
// We now have TWO methods that will be called by the Car
// when sending notifications.
public static void OnCarEngineEvent(string msg)
{
Console.WriteLine("\n***** Message From Car Object *****");
Console.WriteLine("=> {0}", msg);
Console.WriteLine("***********************************\n");
}

public static void OnCarEngineEvent2(string msg)
{
Console.WriteLine("=> {0}", msg.ToUpper());
}
}

Removing Targets from a Delegate’s Invocation List

The Delegate class also defines a static Remove() method that allows a caller to dynamically remove a method from a delegate object’s invocation list. This makes it simple to allow the caller to “unsubscribe” from a given notification at runtime. While you could callDelegate.Remove() directly in code, C# developers can use the -= operator as a convenient shorthand notation. Let’s add a new method to the Car class that allows a caller to remove a method from the invocation list.

public class Car
{
...
public void UnRegisterWithCarEngine(CarEngineHandler methodToCall)
{
listOfHandlers -= methodToCall;
}
}

With the current updates to the Car class, you could stop receiving the engine notification on the second handler by updating Main() as follows:

static void Main(string[] args)
{
Console.WriteLine("***** Delegates as event enablers *****\n");

// First, make a Car object.
Car c1 = new Car("SlugBug", 100, 10);
c1.RegisterWithCarEngine(new Car.CarEngineHandler(OnCarEngineEvent));

// This time, hold onto the delegate object,
// so we can unregister later.
Car.CarEngineHandler handler2 = new Car.CarEngineHandler(OnCarEngineEvent2);
c1.RegisterWithCarEngine(handler2);

// Speed up (this will trigger the events).
Console.WriteLine("***** Speeding up *****");
for (int i = 0; i < 6; i++)
c1.Accelerate(20);

// Unregister from the second handler.
c1.UnRegisterWithCarEngine(handler2);

// We won’t see the "uppercase" message anymore!
Console.WriteLine("***** Speeding up *****");
for (int i = 0; i < 6; i++)
c1.Accelerate(20);

Console.ReadLine();
}

One difference in Main() is that this time you are creating a Car.CarEngineHandler object and storing it in a local variable so you can use this object to unregister with the notification later. Thus, the second time you speed up the Car object, you no longer see the uppercase version of the incoming message data, as you have removed this target from the delegate’s invocation list.

Image Source Code The CarDelegate project is located in the Chapter 10 subdirectory.

Method Group Conversion Syntax

In the previous CarDelegate example, you explicitly created instances of the Car.CarEngineHandler delegate object to register and unregister with the engine notifications.

static void Main(string[] args)
{
Console.WriteLine("***** Delegates as event enablers *****\n");

Car c1 = new Car("SlugBug", 100, 10);
c1.RegisterWithCarEngine(new Car.CarEngineHandler(OnCarEngineEvent));

Car.CarEngineHandler handler2 =
new Car.CarEngineHandler(OnCarEngineEvent2);
c1.RegisterWithCarEngine(handler2);
...
}

To be sure, if you need to call any of the inherited members of MulticastDelegate or Delegate, manually creating a delegate variable is the most straightforward way of doing so. However, in most cases, you don’t really need to hang onto the delegate object. Rather, you typically need to use the delegate object only to pass in the method name as a constructor parameter.

As a simplification, C# provides a shortcut termed method group conversion. This feature allows you to supply a direct method name, rather than a delegate object, when calling methods that take delegates as arguments.

Image Note As you will see later in this chapter, you can also use method group conversion syntax to simplify how you register with a C# event.

To illustrate, create a new Console Application project named CarDelegateMethodGroupConversion and insert the file containing the Car class you defined in the CarDelegate project (and update the namespace name in the Car.cs file to match your new namespace name). Now, consider the following Program class, which uses method group conversion to register and unregister from the engine notifications:

class Program
{
static void Main(string[] args)
{
Console.WriteLine("***** Method Group Conversion *****\n");
Car c1 = new Car();

// Register the simple method name.
c1.RegisterWithCarEngine(CallMeHere);

Console.WriteLine("***** Speeding up *****");
for (int i = 0; i < 6; i++)
c1.Accelerate(20);

// Unregister the simple method name.
c1.UnRegisterWithCarEngine(CallMeHere);

// No more notifications!
for (int i = 0; i < 6; i++)
c1.Accelerate(20);

Console.ReadLine();
}

static void CallMeHere(string msg)
{
Console.WriteLine("=> Message from Car: {0}", msg);
}
}

Notice that you are not directly allocating the associated delegate object but rather simply specifying a method that matches the delegate’s expected signature (a method returning void and taking a single string, in this case). Understand that the C# compiler is still ensuring type safety. Thus, if the CallMeHere() method did not take a string and return void, you would be issued a compiler error.

Image Source Code The CarDelegateMethodGroupConversion project is located in the Chapter 10 subdirectory.

Understanding Generic Delegates

In the previous chapter, I mentioned that C# allows you to define generic delegate types. For example, assume you want to define a delegate type that can call any method returning void and receiving a single parameter. If the argument in question may differ, you could model this using a type parameter. To illustrate, consider the following code within a new Console Application project named GenericDelegate:

namespace GenericDelegate
{
// This generic delegate can represnet any method
// returning void and taking a single parameter of type T.
public delegate void MyGenericDelegate<T>(T arg);

class Program
{
static void Main(string[] args)
{
Console.WriteLine("***** Generic Delegates *****\n");

// Register targets.
MyGenericDelegate<string> strTarget =
new MyGenericDelegate<string>(StringTarget);
strTarget("Some string data");

MyGenericDelegate<int> intTarget =
new MyGenericDelegate<int>(IntTarget);
intTarget(9);
Console.ReadLine();
}

static void StringTarget(string arg)
{
Console.WriteLine("arg in uppercase is: {0}", arg.ToUpper());
}

static void IntTarget(int arg)
{
Console.WriteLine("++arg is: {0}", ++arg);
}
}
}

Notice that MyGenericDelegate<T> defines a single type parameter that represents the argument to pass to the delegate target. When creating an instance of this type, you are required to specify the value of the type parameter, as well as the name of the method the delegate will invoke. Thus, if you specified a string type, you send a string value to the target method.

// Create an instance of MyGenericDelegate<T>
// with string as the type parameter.
MyGenericDelegate<string> strTarget =
new MyGenericDelegate<string>(StringTarget);
strTarget("Some string data");

Given the format of the strTarget object, the StringTarget() method must now take a single string as a parameter.

static void StringTarget(string arg)
{
Console.WriteLine("arg in uppercase is: {0}", arg.ToUpper());
}

Image Source Code The GenericDelegate project is located in the Chapter 10 subdirectory.

The Generic Action<> and Func<> Delegates

Over the course of this chapter, you have seen that when you want to use delegates to enable callbacks in your applications, you typically follow the steps shown here:

· Define a custom delegate that matches the format of the method being pointed to.

· Create an instance of your custom delegate, passing in a method name as a constructor argument.

· Invoke the method indirectly, via a call to Invoke() on the delegate object.

When you take this approach, you typically end up with a number of custom delegates that might never be used beyond the current task at hand (e.g., MyGenericDelegate<T>, CarEngineHandler, and so forth). While it may certainly be the case that you do indeed need to have a custom, uniquely named delegate type for your project, other times the exact name of the delegate type is irrelevant. In many cases, you simply want “some delegate” that takes a set of arguments and possibly has a return value other than void. In these cases, you can use the framework’s built-in Action<> and Func<> delegate types. To illustrate their usefulness, create a new Console Application project named ActionAndFuncDelegates.

The generic Action<> delegate is defined in the System namespaces of mscorlib.dll and System.Core.dll assemblies. You can use this generic delegate to “point to” a method that takes up to 16 arguments (that ought to be enough!) and returns void. Now recall, becauseAction<> is a generic delegate, you will need to specify the underlying types of each parameter as well.

Update your Program class to define a new static method that takes three (or so) unique parameters. Here’s an example:

// This is a target for the Action<> delegate.
static void DisplayMessage(string msg, ConsoleColor txtColor, int printCount)
{
// Set color of console text.
ConsoleColor previous = Console.ForegroundColor;
Console.ForegroundColor = txtColor;

for (int i = 0; i < printCount; i++)
{
Console.WriteLine(msg);
}

// Restore color.
Console.ForegroundColor = previous;
}

Now, rather than building a custom delegate manually to pass the program’s flow to the DisplayMessage() method, you can use the out-of-the-box Action<> delegate, as so:

static void Main(string[] args)
{
Console.WriteLine("***** Fun with Action and Func *****");

// Use the Action<> delegate to point to DisplayMessage.
Action<string, ConsoleColor, int> actionTarget =
new Action<string, ConsoleColor, int>(DisplayMessage);
actionTarget("Action Message!", ConsoleColor.Yellow, 5);

Console.ReadLine();
}

As you can see, using the Action<> delegate saves you the bother of defining a custom delegate type. However, recall that the Action<> delegate type can point only to methods that take a void return value. If you want to point to a method that does have a return value (and don’t want to bother writing the custom delegate yourself), you can use Func<>.

The generic Func<> delegate can point to methods that (like Action<>) take up to 16 parameters and a custom return value. To illustrate, add the following new method to the Program class:

// Target for the Func<> delegate.
static int Add(int x, int y)
{
return x + y;
}

Earlier in the chapter, I had you build a custom BinaryOp delegate to “point to” addition and subtraction methods. However, you can simplify your efforts using a version of Func<> that takes a total of three type parameters. Be aware that the final type parameter of Func<> is alwaysthe return value of the method. Just to solidify that point, assume the Program class also defines the following method:

static string SumToString(int x, int y)
{
return (x + y).ToString();
}

Now, the Main() method can call each of these methods, as so:

Func<int, int, int> funcTarget = new Func<int, int, int>(Add);
int result = funcTarget.Invoke(40, 40);
Console.WriteLine("40 + 40 = {0}", result);

Func<int, int, string> funcTarget2 = new Func<int, int, string>(SumToString);
string sum = funcTarget2(90, 300);
Console.WriteLine(sum);

Also recall method group conversion syntax would allow you to simplify the previous code to the following:

Func<int, int, int> funcTarget = Add;
int result = funcTarget.Invoke(40, 40);
Console.WriteLine("40 + 40 = {0}", result);

Func<int, int, string> funcTarget2 = SumToString;
string sum = funcTarget2(90, 300);
Console.WriteLine(sum);

In any case, given that Action<> and Func<> can save you the step of manually defining a custom delegate, you might be wondering if you should use them all the time. The answer, like so many aspects of programming, is “it depends.” In many cases, Action<> and Func<> will be the preferred course of action (no pun intended). However, if you need a delegate that has a custom name that you feel helps better capture your problem domain, building a custom delegate is as simple as a single code statement. You’ll see both approaches as you work over the remainder of this text.

Image Note Many important .NET APIs make considerable use of Action<> and Func<> delegates, including the parallel programming framework and LINQ (among others).

That wraps up our initial look at the .NET delegate type. You will look at some additional details of working with delegates at the conclusion of this chapter and again in Chapter 19 during your examination of multithreading and asynchronous calls. Next, let’s move on to the related topic of the C# event keyword.

Image Source Code The ActionAndFuncDelegates project is located in the Chapter 10 subdirectory.

Understanding C# Events

Delegates are fairly interesting constructs in that they enable objects in memory to engage in a two-way conversation. However, working with delegates in the raw can entail the creation of some boilerplate code (defining the delegate, declaring necessary member variables, and creating custom registration and unregistration methods to preserve encapsulation, etc.).

Moreover, when you use delegates in the raw as your application’s callback mechanism, if you do not define a class’s delegate member variables as private, the caller will have direct access to the delegate objects. In this case, the caller could reassign the variable to a new delegate object (effectively deleting the current list of functions to call), and, worse yet, the caller would be able to directly invoke the delegate’s invocation list. To illustrate this problem, consider the following reworking (and simplification) of the Car class from the previous CarDelegate example:

public class Car
{
public delegate void CarEngineHandler(string msgForCaller);

// Now a public member!
public CarEngineHandler listOfHandlers;

// Just fire out the Exploded notification.
public void Accelerate(int delta)
{
if (listOfHandlers != null)
listOfHandlers("Sorry, this car is dead...");
}
}

Notice that you no longer have private delegate member variables encapsulated with custom registration methods. Because these members are indeed public, the caller can directly access the listOfHandlers member variable and reassign this type to new CarEngineHandlerobjects and invoke the delegate whenever it so chooses.

class Program
{
static void Main(string[] args)
{
Console.WriteLine("***** Agh! No Encapsulation! *****\n");
// Make a Car.
Car myCar = new Car();
// We have direct access to the delegate!
myCar.listOfHandlers = new Car.CarEngineHandler(CallWhenExploded);
myCar.Accelerate(10);

// We can now assign to a whole new object...
// confusing at best.
myCar.listOfHandlers = new Car.CarEngineHandler(CallHereToo);
myCar.Accelerate(10);

// The caller can also directly invoke the delegate!
myCar.listOfHandlers.Invoke("hee, hee, hee...");
Console.ReadLine();
}

static void CallWhenExploded(string msg)
{ Console.WriteLine(msg); }

static void CallHereToo(string msg)
{ Console.WriteLine(msg); }
}

Exposing public delegate members breaks encapsulation, which not only can lead to code that is hard to maintain (and debug) but could also open your application to possible security risks! Here is the output of the current example:

***** Agh! No Encapsulation! *****

Sorry, this car is dead...
Sorry, this car is dead...
hee, hee, hee...

Obviously, you would not want to give other applications the power to change what a delegate is pointing to or to invoke the members without your permission. Given this, it is common practice to declare private delegate member variables.

Image Source Code The PublicDelegateProblem project is located in the Chapter 10 subdirectory.

The C# event Keyword

As a shortcut, so you don’t have to build custom methods to add or remove methods to a delegate’s invocation list, C# provides the event keyword. When the compiler processes the event keyword, you are automatically provided with registration and unregistration methods, as well as any necessary member variables for your delegate types. These delegate member variables are always declared private, and, therefore, they are not directly exposed from the object firing the event. To be sure, the event keyword can be used to simplify how a custom class sends out notifications to external objects.

Defining an event is a two-step process. First, you need to define a delegate type (or reuse an existing one) that will hold the list of methods to be called when the event is fired. Next, you declare an event (using the C# event keyword) in terms of the related delegate type.

To illustrate the event keyword, create a new Console Application named CarEvents. In this iteration of the Car class, you will define two events named AboutToBlow and Exploded. These events are associated to a single delegate type named CarEngineHandler. Here are the initial updates to the Car class:

public class Car
{
// This delegate works in conjunction with the
// Car’s events.
public delegate void CarEngineHandler(string msg);

// This car can send these events.
public event CarEngineHandler Exploded;
public event CarEngineHandler AboutToBlow;
...
}

Sending an event to the caller is as simple as specifying the event by name, along with any required parameters as defined by the associated delegate. To ensure that the caller has indeed registered with the event, you will want to check the event against a null value before invoking the delegate’s method set. With these points in mind, here is the new iteration of the Car’s Accelerate() method :

public void Accelerate(int delta)
{
// If the car is dead, fire Exploded event.
if (carIsDead)
{
if (Exploded != null)
Exploded("Sorry, this car is dead...");
}
else
{
CurrentSpeed += delta;

// Almost dead?
if (10 == MaxSpeed - CurrentSpeed
&& AboutToBlow != null)
{
AboutToBlow("Careful buddy! Gonna blow!");
}

// Still OK!
if (CurrentSpeed >= MaxSpeed)
carIsDead = true;
else
Console.WriteLine("CurrentSpeed = {0}", CurrentSpeed);
}
}

With this, you have configured the car to send two custom events without having to define custom registration functions or declare delegate member variables. You will see the usage of this new automobile in just a moment, but first let’s check the event architecture in a bit more detail.

Events Under the Hood

When the compiler processes the C# event keyword, it generates two hidden methods, one having an add_ prefix and the other having a remove_ prefix. Each prefix is followed by the name of the C# event. For example, the Exploded event results in two hidden methods namedadd_Exploded() and remove_Exploded() . If you were to check out the CIL instructions behind add_AboutToBlow(), you would find a call to the Delegate.Combine() method. Consider the partial CIL code:

.method public hidebysig specialname instance void
add_AboutToBlow(class CarEvents.Car/CarEngineHandler ’value’) cil managed
{
...
call class [mscorlib]System.Delegate
[mscorlib]System.Delegate::Combine(
class [mscorlib]System.Delegate, class [mscorlib]System.Delegate)
...
}

As you would expect, remove_AboutToBlow() will call Delegate.Remove() on your behalf.

.method public hidebysig specialname instance void
remove_AboutToBlow(class CarEvents.Car/CarEngineHandler ’value’)
cil managed
{
...
call class [mscorlib]System.Delegate
[mscorlib]System.Delegate::Remove(
class [mscorlib]System.Delegate, class [mscorlib]System.Delegate)
...
}

Finally, the CIL code representing the event itself makes use of the .addon and .removeon directives to map the names of the correct add_XXX() and remove_XXX() methods to invoke.

.event CarEvents.Car/EngineHandler AboutToBlow
{
.addon instance void CarEvents.Car::add_AboutToBlow
(class CarEvents.Car/CarEngineHandler)

.removeon instance void CarEvents.Car::remove_AboutToBlow
(class CarEvents.Car/CarEngineHandler)
}

Now that you understand how to build a class that can send C# events (and are aware that events are little more than a typing time-saver), the next big question is how to listen to the incoming events on the caller’s side.

Listening to Incoming Events

C# events also simplify the act of registering the caller-side event handlers. Rather than having to specify custom helper methods, the caller simply uses the += and -= operators directly (which triggers the correct add_XXX() or remove_XXX() method in the background). When you want to register with an event, follow the pattern shown here:

// NameOfObject.NameOfEvent += new RelatedDelegate(functionToCall);
//
Car.CarEngineHandler d = new Car.CarEngineHandler(CarExplodedEventHandler);
myCar.Exploded += d;

When you want to detach from a source of events, use the -= operator, using the following pattern:

// NameOfObject.NameOfEvent -= new RelatedDelegate(functionToCall);
//
myCar.Exploded -= d;

Given these very predictable patterns, here is the refactored Main() method, now using the C# event registration syntax:

class Program
{
static void Main(string[] args)
{
Console.WriteLine("***** Fun with Events *****\n");
Car c1 = new Car("SlugBug", 100, 10);

// Register event handlers.
c1.AboutToBlow += new Car.CarEngineHandler(CarIsAlmostDoomed);
c1.AboutToBlow += new Car.CarEngineHandler(CarAboutToBlow);

Car.CarEngineHandler d = new Car.CarEngineHandler(CarExploded);
c1.Exploded += d;

Console.WriteLine("***** Speeding up *****");
for (int i = 0; i < 6; i++)
c1.Accelerate(20);

// Remove CarExploded method
// from invocation list.
c1.Exploded -= d;

Console.WriteLine("\n***** Speeding up *****");
for (int i = 0; i < 6; i++)
c1.Accelerate(20);
Console.ReadLine();
}

public static void CarAboutToBlow(string msg)
{ Console.WriteLine(msg); }

public static void CarIsAlmostDoomed(string msg)
{ Console.WriteLine("=> Critical Message from Car: {0}", msg); }

public static void CarExploded(string msg)
{ Console.WriteLine(msg); }
}

To even further simplify event registration, you can use method group conversion. Consider the following iteration of Main():

static void Main(string[] args)
{
Console.WriteLine("***** Fun with Events *****\n");
Car c1 = new Car("SlugBug", 100, 10);
// Register event handlers.
c1.AboutToBlow += CarIsAlmostDoomed;
c1.AboutToBlow += CarAboutToBlow;
c1.Exploded += CarExploded;

Console.WriteLine("***** Speeding up *****");
for (int i = 0; i < 6; i++)
c1.Accelerate(20);

c1.Exploded -= CarExploded;

Console.WriteLine("\n***** Speeding up *****");
for (int i = 0; i < 6; i++)
c1.Accelerate(20);

Console.ReadLine();
}

Simplifying Event Registration Using Visual Studio

Visual Studio offers assistance with the process of registering event handlers. When you apply the += syntax during event registration, you will find an IntelliSense window displayed, inviting you to hit the Tab key to autocomplete the associated delegate instance (see Figure 10-2), which is captured using method group conversion syntax.

image

Figure 10-2. Delegate selection IntelliSense

After you hit the Tab key, the IDE will generate the new method automatically, as shown in Figure 10-3.

image

Figure 10-3. Delegate target format IntelliSense

Note the stub code is in the correct format of the delegate target (note that this method has been declared static because the event was registered within a static method).

static void NewCar_AboutToBlow(string msg)
{
// Delete the following line and add your code!
throw new NotImplementedException();
}

IntelliSense is available to all .NET events in the base class libraries. This IDE feature is a massive time-saver, given that it saves you from having to search the .NET help system to figure out both the correct delegate to use with a particular event and the format of the delegate target method.

Cleaning Up Event Invocation Using the C# 6.0 Null-Conditional Operator

In the current example, you most likely noticed that before you fired an event to any listener, you made sure to check for null. This is important given that if nobody is listening for your event but you fire it anyway, you will receive a null reference exception at runtime. While important, you might agree it is a bit clunky to make numerous conditional checks against null.

Thankfully, with the current release of C#, you can leverage the null conditional operator (?) which essentially performs this sort of check automatically. Be aware, when using this new simplified syntax, you must manually call the Invoke() method of the underlying delegate. For example, rather than saying this:

// If the car is dead, fire Exploded event.
if (carIsDead)
{
if (Exploded != null)
Exploded("Sorry, this car is dead...");
}

We can now simply say the following:

// If the car is dead, fire Exploded event.
if (carIsDead)
{
Exploded?.Invoke("Sorry, this car is dead...");
}

you could also update the code that fires the AboutToBlow event in a similar manner (note here I moved the check for null out of the original if statement):

// Almost dead?
if (10 == MaxSpeed - CurrentSpeed)
{
AboutToBlow?.Invoke("Careful buddy! Gonna blow!");
}

Because of the simplified syntax, you are likely to favor the null conditional operator when firing events. However, it is still perfectly acceptable to manually check for null when necessary.

Image Source Code The CarEvents project is located in the Chapter 10 subdirectory.

Creating Custom Event Arguments

Truth be told, there is one final enhancement you could make to the current iteration of the Car class that mirrors Microsoft’s recommended event pattern. As you begin to explore the events sent by a given type in the base class libraries, you will find that the first parameter of the underlying delegate is a System.Object, while the second parameter is a descendant of System.EventArgs.

The System.Object argument represents a reference to the object that sent the event (such as the Car), while the second parameter represents information regarding the event at hand. The System.EventArgs base class represents an event that is not sending any custom information.

public class EventArgs
{
public static readonly EventArgs Empty;
public EventArgs();
}

For simple events, you can pass an instance of EventArgs directly. However, when you want to pass along custom data, you should build a suitable class deriving from EventArgs. For this example, assume you have a class named CarEventArgs, which maintains a string representing the message sent to the receiver.

public class CarEventArgs : EventArgs
{
public readonly string msg;
public CarEventArgs(string message)
{
msg = message;
}
}

With this, you would now update the CarEngineHandler delegate type definition as follows (the events would be unchanged):

public class Car
{
public delegate void CarEngineHandler(object sender, CarEventArgs e);
...
}

Here, when firing the events from within the Accelerate() method, you would now need to supply a reference to the current Car (via the this keyword) and an instance of the CarEventArgs type. For example, consider the following partial update:

public void Accelerate(int delta)
{
// If the car is dead, fire Exploded event.
if (carIsDead)
{
Exploded?.Invoke(this, new CarEventArgs("Sorry, this car is dead..."));
}
...
}

On the caller’s side, all you would need to do is update your event handlers to receive the incoming parameters and obtain the message via the read-only field. Here’s an example:

public static void CarAboutToBlow(object sender, CarEventArgs e)
{
Console.WriteLine("{0} says: {1}", sender, e.msg);
}

If the receiver wants to interact with the object that sent the event, you can explicitly cast the System.Object. From this reference, you can make use of any public member of the object that sent the event notification.

public static void CarAboutToBlow(object sender, CarEventArgs e)
{
// Just to be safe, perform a
// runtime check before casting.
if (sender is Car)
{
Car c = (Car)sender;
Console.WriteLine("Critical Message from {0}: {1}", c.PetName, e.msg);
}
}

Image Source Code The CarEventArgs project is located in the Chapter 10 subdirectory.

The Generic EventHandler<T> Delegate

Given that so many custom delegates take an object as the first parameter and an EventArgs descendant as the second, you could further streamline the previous example by using the generic EventHandler<T> type, where T is your custom EventArgs type. Consider the following update to the Car type (notice how you no longer need to define a custom delegate type at all):

public class Car
{
public event EventHandler<CarEventArgs> Exploded;
public event EventHandler<CarEventArgs> AboutToBlow;
...
}

The Main() method could then use EventHandler<CarEventArgs> anywhere you previously specified CarEventHandler (or, once again, use method group conversion).

static void Main(string[] args)
{
Console.WriteLine("***** Prim and Proper Events *****\n");

// Make a car as usual.
Car c1 = new Car("SlugBug", 100, 10);

// Register event handlers.
c1.AboutToBlow += CarIsAlmostDoomed;
c1.AboutToBlow += CarAboutToBlow;

EventHandler<CarEventArgs> d = new EventHandler<CarEventArgs>(CarExploded);
c1.Exploded += d;
...
}

Great! At this point, you have seen the core aspects of working with delegates and events in the C# language. While you could use this information for just about all your callback needs, you will wrap up this chapter with a look at some final simplifications, specifically anonymous methods and lambda expressions.

Image Source Code The GenericCarEventArgs project is located in the Chapter 10 subdirectory.

Understanding C# Anonymous Methods

As you have seen, when a caller wants to listen to incoming events, it must define a custom method in a class (or structure) that matches the signature of the associated delegate. Here’s an example:

class Program
{
static void Main(string[] args)
{
SomeType t = new SomeType();

// Assume "SomeDelegate" can point to methods taking no
// args and returning void.
t.SomeEvent += new SomeDelegate(MyEventHandler);
}

// Typically only called by the SomeDelegate object.
public static void MyEventHandler()
{
// Do something when event is fired.
}
}

When you think about it, however, methods such as MyEventHandler() are seldom intended to be called by any part of the program other than the invoking delegate. As far as productivity is concerned, it is a bit of a bother (though in no way a showstopper) to manually define a separate method to be called by the delegate object.

To address this point, it is possible to associate an event directly to a block of code statements at the time of event registration. Formally, such code is termed an anonymous method. To illustrate the syntax, check out the following Main() method, which handles the events sent from theCar class using anonymous methods, rather than specifically named event handlers:

class Program
{
static void Main(string[] args)
{
Console.WriteLine("***** Anonymous Methods *****\n");
Car c1 = new Car("SlugBug", 100, 10);

// Register event handlers as anonymous methods.
c1.AboutToBlow += delegate
{
Console.WriteLine("Eek! Going too fast!");
};

c1.AboutToBlow += delegate(object sender, CarEventArgs e)
{
Console.WriteLine("Message from Car: {0}", e.msg);
};

c1.Exploded += delegate(object sender, CarEventArgs e)
{
Console.WriteLine("Fatal Message from Car: {0}", e.msg);
};

// This will eventually trigger the events.
for (int i = 0; i < 6; i++)
c1.Accelerate(20);

Console.ReadLine();
}
}

Image Note The final curly bracket of an anonymous method must be terminated by a semicolon. If you fail to do so, you are issued a compilation error.

Again, notice that the Program type no longer defines specific static event handlers such as CarAboutToBlow() or CarExploded(). Rather, the unnamed (aka anonymous) methods are defined inline at the time the caller is handling the event using the += syntax. The basic syntax of an anonymous method matches the following pseudocode:

class Program
{
static void Main(string[] args)
{
SomeType t = new SomeType();
t.SomeEvent += delegate (optionallySpecifiedDelegateArgs)
{ /* statements */ };
}
}

When handling the first AboutToBlow event within the previous Main() method, notice that you are not specifying the arguments passed from the delegate.

c1.AboutToBlow += delegate
{
Console.WriteLine("Eek! Going too fast!");
};

Strictly speaking, you are not required to receive the incoming arguments sent by a specific event. However, if you want to make use of the possible incoming arguments, you will need to specify the parameters prototyped by the delegate type (as shown in the second handling of theAboutToBlow and Exploded events). Here’s an example:

c1.AboutToBlow += delegate(object sender, CarEventArgs e)
{
Console.WriteLine("Critical Message from Car: {0}", e.msg);
};

Accessing Local Variables

Anonymous methods are interesting in that they are able to access the local variables of the method that defines them. Formally speaking, such variables are termed outer variables of the anonymous method. A few important points about the interaction between an anonymous method scope and the scope of the defining method should be mentioned.

· An anonymous method cannot access ref or out parameters of the defining method.

· An anonymous method cannot have a local variable with the same name as a local variable in the outer method.

· An anonymous method can access instance variables (or static variables, as appropriate) in the outer class scope.

· An anonymous method can declare local variables with the same name as outer class member variables (the local variables have a distinct scope and hide the outer class member variables).

Assume your Main() method defined a local integer named aboutToBlowCounter. Within the anonymous methods that handle the AboutToBlow event, you will increment this counter by one and print out the tally before Main() completes.

static void Main(string[] args)
{
Console.WriteLine("***** Anonymous Methods *****\n");
int aboutToBlowCounter = 0;

// Make a car as usual.
Car c1 = new Car("SlugBug", 100, 10);

// Register event handlers as anonymous methods.
c1.AboutToBlow += delegate
{
aboutToBlowCounter++;
Console.WriteLine("Eek! Going too fast!");
};

c1.AboutToBlow += delegate(object sender, CarEventArgs e)
{
aboutToBlowCounter++;
Console.WriteLine("Critical Message from Car: {0}", e.msg);
};

// This will eventually trigger the events.
for (int i = 0; i < 6; i++)
c1.Accelerate(20);

Console.WriteLine("AboutToBlow event was fired {0} times.",
aboutToBlowCounter);
Console.ReadLine();
}

After you run this updated Main() method, you will find the final Console.WriteLine() reports the AboutToBlow event was fired twice.

Image Source Code The AnonymousMethods project is located in the Chapter 10 subdirectory.

Understanding Lambda Expressions

To conclude your look at the .NET event architecture, you will examine C# lambda expressions. As just explained, C# supports the ability to handle events “inline” by assigning a block of code statements directly to an event using anonymous methods, rather than building a stand-alone method to be called by the underlying delegate. Lambda expressions are nothing more than a concise way to author anonymous methods and ultimately simplify how you work with the .NET delegate type.

To set the stage for your examination of lambda expressions, create a new Console Application project named SimpleLambdaExpressions. To begin, consider the FindAll() method of the generic List<T> class. This method can be called when you need to extract a subset of items from the collection and is prototyped like so:

// Method of the System.Collections.Generic.List<T> class.
public List<T> FindAll(Predicate<T> match)

As you can see, this method returns a new List<T> that represents the subset of data. Also notice that the sole parameter to FindAll() is a generic delegate of type System.Predicate<T>. This delegate type can point to any method returning a bool and takes a single type parameter as the only input parameter.

// This delegate is used by FindAll() method
// to extract out the subset.
public delegate bool Predicate<T>(T obj);

When you call FindAll(), each item in the List<T> is passed to the method pointed to by the Predicate<T> object. The implementation of said method will perform some calculations to see whether the incoming data matches the necessary criteria and will return true orfalse. If this method returns true, the item will be added to the new List<T> that represents the subset (got all that?).

Before you see how lambda expressions can simplify working with FindAll(), let’s work the problem out in longhand notation, using the delegate objects directly. Add a method (named TraditionalDelegateSyntax()) within your Program type that interacts with theSystem.Predicate<T> type to discover the even numbers in a List<T> of integers.

class Program
{
static void Main(string[] args)
{
Console.WriteLine("***** Fun with Lambdas *****\n");
TraditionalDelegateSyntax();
Console.ReadLine();
}

static void TraditionalDelegateSyntax()
{
// Make a list of integers.
List<int> list = new List<int>();
list.AddRange(new int[] { 20, 1, 4, 8, 9, 44 });

// Call FindAll() using traditional delegate syntax.
Predicate<int> callback = IsEvenNumber;
List<int> evenNumbers = list.FindAll(callback);

Console.WriteLine("Here are your even numbers:");
foreach (int evenNumber in evenNumbers)
{
Console.Write("{0}\t", evenNumber);
}
Console.WriteLine();
}

// Target for the Predicate<> delegate.
static bool IsEvenNumber(int i)
{
// Is it an even number?
return (i % 2) == 0;
}
}

Here, you have a method (IsEvenNumber()) that is in charge of testing the incoming integer parameter to see whether it is even or odd via the C# modulo operator, %. If you execute your application, you will find the numbers 20, 4, 8, and 44 print to the console.

While this traditional approach to working with delegates behaves as expected, the IsEvenNumber() method is invoked only in limited circumstances—specifically when you call FindAll(), which leaves you with the baggage of a full method definition. If you were to instead use an anonymous method, your code would clean up considerably. Consider the following new method of the Program class:

static void AnonymousMethodSyntax()
{
// Make a list of integers.
List<int> list = new List<int>();
list.AddRange(new int[] { 20, 1, 4, 8, 9, 44 });

// Now, use an anonymous method.
List<int> evenNumbers = list.FindAll(delegate(int i)
{ return (i % 2) == 0; } );

Console.WriteLine("Here are your even numbers:");
foreach (int evenNumber in evenNumbers)
{
Console.Write("{0}\t", evenNumber);
}
Console.WriteLine();
}

In this case, rather than directly creating a Predicate<T> delegate object and then authoring a stand- alone method, you are able to inline a method anonymously. While this is a step in the right direction, you are still required to use the delegate keyword (or a strongly typedPredicate<T>), and you must ensure that the parameter list is a dead-on match.

List<int> evenNumbers = list.FindAll(
delegate(int i)
{
return (i % 2) == 0;
}
);

Lambda expressions can be used to simplify the call to FindAll() even more. When you use lambda syntax, there is no trace of the underlying delegate object whatsoever. Consider the following new method to the Program class:

static void LambdaExpressionSyntax()
{
// Make a list of integers.
List<int> list = new List<int>();
list.AddRange(new int[] { 20, 1, 4, 8, 9, 44 });

// Now, use a C# lambda expression.
List<int> evenNumbers = list.FindAll(i => (i % 2) == 0);

Console.WriteLine("Here are your even numbers:");
foreach (int evenNumber in evenNumbers)
{
Console.Write("{0}\t", evenNumber);
}
Console.WriteLine();
}

In this case, notice the rather strange statement of code passed into the FindAll() method, which is in fact a lambda expression. In this iteration of the example, there is no trace whatsoever of the Predicate<T> delegate (or the delegate keyword, for that matter). All you have specified is the lambda expression.

i => (i % 2) == 0

Before I break this syntax down, first understand that lambda expressions can be used anywhere you would have used an anonymous method or a strongly typed delegate (typically with far fewer keystrokes). Under the hood, the C# compiler translates the expression into a standard anonymous method making use of the Predicate<T> delegate type (which can be verified using ildasm.exe or reflector.exe). Specifically, the following code statement:

// This lambda expression...
List<int> evenNumbers = list.FindAll(i => (i % 2) == 0);

is compiled into the following approximate C# code:

// ...becomes this anonymous method.
List<int> evenNumbers = list.FindAll(delegate (int i)
{
return (i % 2) == 0;
});

Dissecting a Lambda Expression

A lambda expression is written by first defining a parameter list, followed by the => token (C#’s token for the lambda operator found in the lambda calculus), followed by a set of statements (or a single statement) that will process these arguments. From a high level, a lambda expression can be understood as follows:

ArgumentsToProcess => StatementsToProcessThem

Within the LambdaExpressionSyntax() method, things break down like so:

// "i" is our parameter list.
// "(i % 2) == 0" is our statement set to process "i".
List<int> evenNumbers = list.FindAll(i => (i % 2) == 0);

The parameters of a lambda expression can be explicitly or implicitly typed. Currently, the underlying data type representing the i parameter (an integer) is determined implicitly. The compiler is able to figure out that i is an integer based on the context of the overall lambda expression and the underlying delegate. However, it is also possible to explicitly define the type of each parameter in the expression, by wrapping the data type and variable name in a pair of parentheses, as follows:

// Now, explicitly state the parameter type.
List<int> evenNumbers = list.FindAll((int i) => (i % 2) == 0);

As you have seen, if a lambda expression has a single, implicitly typed parameter, the parentheses may be omitted from the parameter list. If you want to be consistent regarding your use of lambda parameters, you can always wrap the parameter list within parentheses, leaving you with this expression:

List<int> evenNumbers = list.FindAll((i) => (i % 2) == 0);

Finally, notice that currently the expression has not been wrapped in parentheses (you have of course wrapped the modulo statement to ensure it is executed first before the test for equality). Lambda expressions do allow for the statement to be wrapped as follows:

// Now, wrap the expression as well.
List<int> evenNumbers = list.FindAll((i) => ((i % 2) == 0));

Now that you have seen the various ways to build a lambda expression, how can you read this lambda statement in human-friendly terms? Leaving the raw mathematics behind, the following explanation fits the bill:

// My list of parameters (in this case, a single integer named i)
// will be processed by the expression (i % 2) == 0.
List<int> evenNumbers = list.FindAll((i) => ((i % 2) == 0));

Processing Arguments Within Multiple Statements

The first lambda expression was a single statement that ultimately evaluated to a Boolean. However, as you know, many delegate targets must perform a number of code statements. For this reason, C# allows you to build lambda expressions using multiple statement blocks. When your expression must process the parameters using multiple lines of code, you can do so by denoting a scope for these statements using the expected curly brackets. Consider the following example update to the LambdaExpressionSyntax() method:

static void LambdaExpressionSyntax()
{
// Make a list of integers.
List<int> list = new List<int>();
list.AddRange(new int[] { 20, 1, 4, 8, 9, 44 });

// Now process each argument within a group of
// code statements.
List<int> evenNumbers = list.FindAll((i) =>
{
Console.WriteLine("value of i is currently: {0}", i);
bool isEven = ((i % 2) == 0);
return isEven;
});

Console.WriteLine("Here are your even numbers:");
foreach (int evenNumber in evenNumbers)
{
Console.Write("{0}\t", evenNumber);
}
Console.WriteLine();
}

In this case, the parameter list (again, a single integer named i) is being processed by a set of code statements. Beyond the calls to Console.WriteLine(), the modulo statement has been broken into two code statements for increased readability. Assuming each of the methods you’ve looked at in this section are called from within Main():

static void Main(string[] args)
{
Console.WriteLine("***** Fun with Lambdas *****\n");
TraditionalDelegateSyntax();
AnonymousMethodSyntax();
Console.WriteLine();
LambdaExpressionSyntax();
Console.ReadLine();
}

you will find the following output:

***** Fun with Lambdas *****
Here are your even numbers:
20 4 8 44
Here are your even numbers:
20 4 8 44
value of i is currently: 20
value of i is currently: 1
value of i is currently: 4
value of i is currently: 8
value of i is currently: 9
value of i is currently: 44
Here are your even numbers:
20 4 8 44

Image Source Code The SimpleLambdaExpressions project can be found in the Chapter 10 subdirectory.

Lambda Expressions with Multiple (or Zero) Parameters

The lambda expressions you have seen here processed a single parameter. This is not a requirement, however, as a lambda expression may process multiple arguments (or none). To illustrate the first scenario, create a Console Application project named LambdaExpressionsMultipleParams. Next, assume the following incarnation of the SimpleMath type:

public class SimpleMath
{
public delegate void MathMessage(string msg, int result);
private MathMessage mmDelegate;

public void SetMathHandler(MathMessage target)
{mmDelegate = target; }

public void Add(int x, int y)
{
mmDelegate?.Invoke("Adding has completed!", x + y);
}
}

Notice that the MathMessage delegate type is expecting two parameters. To represent them as a lambda expression, the Main() method might be written as follows:

static void Main(string[] args)
{
// Register with delegate as a lambda expression.
SimpleMath m = new SimpleMath();
m.SetMathHandler((msg, result) =>
{Console.WriteLine("Message: {0}, Result: {1}", msg, result);});

// This will execute the lambda expression.
m.Add(10, 10);
Console.ReadLine();
}

Here, you are leveraging type inference, as the two parameters have not been strongly typed for simplicity. However, you could call SetMathHandler(), as follows:

m.SetMathHandler((string msg, int result) =>
{Console.WriteLine("Message: {0}, Result: {1}", msg, result);});

Finally, if you are using a lambda expression to interact with a delegate taking no parameters at all, you may do so by supplying a pair of empty parentheses as the parameter. Thus, assuming you have defined the following delegate type:

public delegate string VerySimpleDelegate();

you could handle the result of the invocation as follows:

// Prints "Enjoy your string!" to the console.
VerySimpleDelegate d = new VerySimpleDelegate( () => {return "Enjoy your string!";} );
Console.WriteLine(d());

Image Source Code The LambdaExpressionsMultipleParams project can be found in the Chapter 10 subdirectory.

Retrofitting the CarEvents Example Using Lambda Expressions

Given that the whole reason for lambda expressions is to provide a clean, concise manner to define an anonymous method (and therefore indirectly a manner to simplify working with delegates), let’s retrofit the CarEventArgs project created earlier in this chapter. Here is a simplified version of that project’s Program class, which makes use of lambda expression syntax (rather than the raw delegates) to hook into each event sent from the Car object:

static void Main(string[] args)
{
Console.WriteLine("***** More Fun with Lambdas *****\n");

// Make a car as usual.
Car c1 = new Car("SlugBug", 100, 10);

// Hook into events with lambdas!
c1.AboutToBlow += (sender, e) => { Console.WriteLine(e.msg);};
c1.Exploded += (sender, e) => { Console.WriteLine(e.msg); };

// Speed up (this will generate the events).
Console.WriteLine("\n***** Speeding up *****");
for (int i = 0; i < 6; i++)
c1.Accelerate(20);

Console.ReadLine();
}

Lambdas and Single Statement Member Implementations

The final point to be made about the C# lambda operator is that, as of .NET 4.6, it is now permissible to use the => operator to simplify some (but not all) member implementations. Specifically, if you have a method or property (in addition to a custom operator or conversion routine; seeChapter 11) that consists of exactly a single line of code in the implementation, you are not required to define a scope via curly bracket. You can instead leverage the lambda operator.

Consider the previous code example where you wired in code to handle the AboutToBlow and Exploded events. Note how you defined a curly-bracket scope to capture the Console.WriteLine() method calls. If you like, you could now simply write the following:

c1.AboutToBlow += (sender, e) => Console.WriteLine(e.msg);
c1.Exploded += (sender, e) => Console.WriteLine(e.msg);

Be aware, however, this new shortened syntax can be use anywhere at all, even when your code has nothing to do with delegates or events. So for example, if you were to build a trivial class to add two numbers, you might write the following:

class SimpleMath
{
public int Add(int x, int y)
{
return x + y;
}

public void PrintSum(int x, int y)
{
Console.WriteLine(x + y);
}
}

Alternatively, you could now write code like the following:

class SimpleMath
{
public int Add(int x, int y) => x + y;
public void PrintSum(int x, int y) => Console.WriteLine(x + y);
}

Ideally, at this point you can see the overall role of lambda expressions and understand how they provide a “functional manner” to work with anonymous methods and delegate types. Although the lambda operator (=>) might take a bit to get used to, always remember a lambda expression can be broken down to the following simple equation:

ArgumentsToProcess => StatementsToProcessThem

Or, if using the => operator to implement a single line type member, it would be like this:

TypeMember => SingleCodeStatement

It is worth pointing out that the LINQ programming model also makes substantial use of lambda expressions to help simplify your coding efforts. You will examine LINQ beginning in Chapter 12.

Image Source Code The CarEventsWithLambdas project can be found in the Chapter 10 subdirectory.

Summary

In this chapter, you examined a number of ways in which multiple objects can partake in a bidirectional conversation. First, you looked at the C# delegate keyword, which is used to indirectly construct a class derived from System.MulticastDelegate. As you saw, a delegate object maintains a list of methods to call when told to do so. These invocations may be made synchronously (using the Invoke() method) or asynchronously (via the BeginInvoke() and EndInvoke() methods). Again, the asynchronous nature of .NET delegate types will be examined inChapter 19.

You then examined the C# event keyword, which, when used in conjunction with a delegate type, can simplify the process of sending your event notifications to waiting callers. As shown via the resulting CIL, the .NET event model maps to hidden calls on theSystem.Delegate/System.MulticastDelegate types. In this light, the C# event keyword is purely optional in that it simply saves you some typing time. As well, you have seen that the C# 6.0 null conditional operator simplifies how you safely fire events to any interested party.

This chapter also explored a C# language feature termed anonymous methods. Using this syntactic construct, you are able to directly associate a block of code statements to a given event. As you have seen, anonymous methods are free to ignore the parameters sent by the event and have access to the “outer variables” of the defining method. You also examined a simplified way to register events using method group conversion.

Finally, you wrapped things up by looking at the C# lambda operator, =>. As shown, this syntax is a great shorthand notation for authoring anonymous methods, where a stack of arguments can be passed into a group of statements for processing. Any method in the .NET platform that takes a delegate object as an argument can be substituted with a related lambda expression, which will typically simplify your code base quite a bit.