In the previous lesson I showed you how the compiler actually implements events by actually adding two methods named addon() and removeon() in the background and making the Action field private, so we can’t invoke it. But that was done in MSIL language, and we really don’t need to deal with such a low level. In this lesson, I will show you how we can do the exact same thing using the usual C# language, in case we need more control over how our events are implemented.
Let’s take again the code that we had in the previous lesson:
1 2 3 4 5 6 7 8 9 |
using System; namespace HelloWorld { public class TrainSignal { public Action TrainsArecoming; } } |
And now, let’s re-trace all the steps that the compiler did automatically – first, let’s make our Action private, so it can’t be invoked directly:
1 2 3 4 5 6 7 8 9 |
using System; namespace HelloWorld { public class TrainSignal { private Action trainsArecoming; } } |
I’ve also changed the name of the Action to start with a lowercase character, just so we won’t have a naming conflict later on.
Step two – add a public event:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
using System; namespace HelloWorld { public class TrainSignal { private Action trainsArecoming; public event Action TrainsArecoming { add { } remove { } } } } |
You will certainly notice the new way of declaring my delegate: suddenly, my event declaration has a body, and inside it, I am adding two constructs, add and remove. This is the C# way of allowing us to do just the same thing as addon() and removeon() were doing automatically. It is in these constructs that we can subscribe and unsubscribe to our private Action, by using a ghost parameter named value (also present when declaring a property). To test this, let’s test that these constructs are executed when something subscribes or unsubscribes to our events:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
using System; namespace HelloWorld { public class TrainSignal { private Action trainsArecoming; public event Action TrainsArecoming { add { // add subscriber to the private Action trainsArecoming += value; Console.WriteLine("Subscriber was added!"); } remove { // remove subscriber from the private Action trainsArecoming -= value; Console.WriteLine("Subscriber was removed!"); } } public void TriggerTrainSignal() { // call all subscribers if (trainsArecoming != null) // make sure there are subscribers! trainsArecoming(); // invoke the Action } } public class Program { static void Main() { TrainSignal trainSignal = new TrainSignal(); // add an event handler (a subscribing method that will be called when the event is triggered) trainSignal.TrainsArecoming += () => Console.WriteLine("I was notified by the event!"); // trigger the train signal trainSignal.TriggerTrainSignal(); } } } |
The explanation is simple: we are subscribing a method to a private Action by using the add and remove event constructs, which is the exact same thing that the compiler does automatically, if we don’t specify add and remove blocks. But, by specifying them, we have a greater control, because we can suddenly implement logic when adding or removing to an event, like, for example, checking if the subscriber method meets some requirements, or logging the actions, or subscribing the method more than once, or whatever.
The result of the above code will look like this:
At this point, you can kind of start to see that the event is just an interface towards our private Action, so that we are in control of who and when invokes that Action, and by using the add and remove event members, we can also control who can subscribe and unsubscribe to the Action, when it can do so, under what conditions, etc. This really gives us all the control we could want when it comes to securely playing with delegates.
True, most of the times, you won’t need this degree of control, using an event in a simple manner, like in the previous lesson, is more than enough for most daily life cases.
Tags: Action, add, addon, delegates, events, remove, removeon