Functional programming with C# - Events

Events in C# are declared using delegates and are a nice way to notify a client about something that has happened 

This is the sixth part of the series about functional programming with C#. If you have not read through the first part about delegates, the second part about anonymous functions, the third part about lambda, the fourth part about Action delegates or the fifth part about Func delegates I recommend you to learn about them before continuing.

By now you understand that delegates are a C# type created to store references to methods with a specific signature and you know that C# provides delegates Action for void methods and Func for methods that return something. When passing references to methods as arguments we usually call these methods “callback” since they’re executable code that is passed as an argument to another code.
Before getting started with events, let's imagine we have to provide a solution to identify prime numbers within a given range and count the total. We could use our SmartClass to implement this logic.

We could invoke this code with:
Total Prime Numbers: 47

Let's imagine now that we also want to print on screen each one of the prime numbers found in addition to the total. We could save all the prime numbers found in a list and return it so that we can later on iterate and print each prime number, but we could also use delegates to provide a real-time solution in which whenever a prime number is found we could print the number and continue searching. We would have to declare a delegate to store void methods that accept an int parameter (the prime number to print), and then have the method that counts the total of prime numbers accept a callback method using the delegate so that whenever a prime number has been found this callback method code can be executed:
Then in our client we simply create the method that is to be executed each time a prime number is found and pass its reference to SmartClass:
But hey! can't we use a lambda expression to pass directly an anonymous function as the callback function? Well, since we're not planning to use the WritePrimeNumberFound code anywhere else, yes we can! You can get rid of the WritePrimeNumberFound method and have the call to smartClass.GetTotalPrimeNumbers pass a lambda expression with the anonymous callback method to execute:
This would result on
Prime number found = 1
Prime number found = 2
Prime number found = 3
Prime number found = 5
Prime number found = 7
Prime number found = 11
Prime number found = 13
Prime number found = 17
Prime number found = 19
Total Prime Numbers: 9
which is what we wanted to get.


Events

The solution provided with callback method using a delegate is valid, but there is a better solution that does not require having callback methods or using delegate properties. A class in C# usually has properties and methods, but there is another member a class can have: events.

An event is a way for a class to provide notifications to clients of that class when some interesting thing happens to an object. In other words, our SmartClass can benefit from using events to notify any client that a prime number has been found.

Events are declared using delegates because the event's goal is to invoke all the methods its delegate contains. If you are familiar with design patterns this will remind you of the observer pattern where clients subscribe to a specific event and are notified when the event happens.

We can declare an event to be triggered by SmartClass when a prime number has been found.
As mentioned before, notice that an event requires a delegate so that it can execute all the methods this delegate contains. Our delegate can be declared outside the class and the event as a member of the class. Also notice that good practices recommend to call the delegate with the suffix event handler and an event with a meaningful name that describes what has happened.

We can modify the GetTotalPrimeNumbers to remove the need to accept the callback method. Instead we can simply invoke the event if it's not null.
That's how SmartClass notifies clients that a prime number has been found. It is up to each client to decide how to handle this event. In our case we will use a method as the on prime found handler.
would display the same result as before, only his time achieved through an event instead a callback function.

Prime number found = 1
Prime number found = 2
Prime number found = 3
Prime number found = 5
Prime number found = 7
Prime number found = 11
Prime number found = 13
Prime number found = 17
Prime number found = 19
Total Prime Numbers: 9
The good practices also say that when using events, in addition to declaring our custom delegates, this delegate should have its signature as void return type and it should accept 2 arguments: the object sender that generated the event and the event args that contain any information the handler methods require. If the handlers (the methods that match the delegate's signature) require some argument it's also recommended to implement our own class extending EventArgs to wrap them.

Let's modify our delegate following these good practices:
and let's modify the way the event is invoked by passing a reference to this object (because the current object would be the sender) and an instance of the custom event arguments wrapper PrimeNumberEventArgs that contains the prime number.
Finally in our client let's modify the method we want to add as event handler to match the delegate signature:
As expected this would result on:
Iberodev.Functional.Event.SmartClass informs: Prime number found = 1
Iberodev.Functional.Event.SmartClass informs: Prime number found = 2
Iberodev.Functional.Event.SmartClass informs: Prime number found = 3
Iberodev.Functional.Event.SmartClass informs: Prime number found = 5
Iberodev.Functional.Event.SmartClass informs: Prime number found = 7
Iberodev.Functional.Event.SmartClass informs: Prime number found = 11
Iberodev.Functional.Event.SmartClass informs: Prime number found = 13
Iberodev.Functional.Event.SmartClass informs: Prime number found = 17
Iberodev.Functional.Event.SmartClass informs: Prime number found = 19
Total Prime Numbers: 9

Find the examples on my GitLab repository

Comments