Event-driven programming, delegates and events.
So far you have been learning about delegates. By putting what we learned in action we can already create event driven programming functionalities.
We will also talk about publishers and subscribers and the event keyword.
But for now check the example below where we use a delegate to handle score updates. This could be a game and when a score happens we can call the delegate method and let call 2 methods. One for updating the current score and one for updating the database.
using System;
class UpdateDatabase {
public static void updateScoreBoard(int score) {
Console.WriteLine("Updating database scoreboard...");
}
}
class CurrentScore {
static int _score = 0;
public static void updateScore(int score) {
_score += score;
Console.WriteLine("Your current score: " + score);
}
}
class Program {
public delegate void Score(int score);
public static void Main (string[] args) {
Score updateCurrentScore = CurrentScore.updateScore;
Score updateScoreBoard = UpdateDatabase.updateScoreBoard;
Score score = updateCurrentScore + updateScoreBoard;
score(10);
}
}
Publisher and subscriber pattern.
Even though the code we just wrote works perfectly fine, we want to follow best practices and use the sender and subscriber pattern. This will make our code
We will use the keyword event
and adopt it.
I will start with a simple example to show you how it works.
class Publisher {
// declare a delegate with two arguments, the sendsr and the EventArgs obj
public delegate void EventHandler(object sender, EventArgs e);
// use event keyword followed by the delegate object
public event EventHandler myEvent;
public void fireEvent() {
// if there is a subscriber, invoke
if(myEvent != null){
myEvent(this, EventArgs.Empty);
}
}
}
class Subscriber {
// Subscriber method revieves a sender and the args theough the EventArgs object
public void IAmCalled(object sender, EventArgs e) {
Console.WriteLine("I am called");
}
}
class Program {
public static void Main (string[] args) {
// instances of the publisher and subscriber
Publisher publisher = new Publisher();
Subscriber subscriber = new Subscriber();
// assign the subscriber to the publisher
publisher.myEvent += subscriber.IAmCalled;
publisher.fireEvent();
}
}
The take away here is that we use the event keyword, EventArgs object for arguments and then subscribe to the event delegate object.
This is not complete though. We want to improve our code.
We don’t need to declare our own EventHandler delegete object. .Net has a built in EventHandler object we can use.
We do not want the method that fires the event to be public but rather they are normally protected and virtual so that children classes can override the method and protected from outside the class or it’s children.
If we have arguments we can create a class that inherit from EventArgs and add our own fields.
Let’s implement the same code as in our first example with the scores but with best practices.
class ScoreUpdatedEventArgs : EventArgs
{
public int Score { get; set;}
}
class ScoreManager
{
public event EventHandler<ScoreUpdatedEventArgs> ScoreUpdated;
public void AddScore(int score)
{
OnScoreUpdated(new ScoreUpdatedEventArgs { Score = score });
}
protected virtual void OnScoreUpdated(ScoreUpdatedEventArgs e)
{
ScoreUpdated?.Invoke(this, e);
}
}
class UpdateDatabase
{
public void OnScoreUpdated(object sender, ScoreUpdatedEventArgs e)
{
Console.WriteLine("Updating database scoreboard with score: " + e.Score);
}
}
class CurrentScore
{
public void OnScoreUpdated(object sender, ScoreUpdatedEventArgs e)
{
Console.WriteLine("Your current score: " + e.Score);
}
}
class Program
{
public static void Main(string[] args)
{
ScoreManager scoreManager = new ScoreManager();
UpdateDatabase updateDatabase = new UpdateDatabase();
CurrentScore currentScore = new CurrentScore();
// Subscribe to the event
scoreManager.ScoreUpdated += updateDatabase.OnScoreUpdated;
scoreManager.ScoreUpdated += currentScore.OnScoreUpdated;
// Add score and raise the event
scoreManager.AddScore(10);
}
}
Benefits of the publisher subscriber pattern
There is a couple of benefits of writing the code according to this pattern and how C# implemented it.
- The method that publish the event is encapsulated and only acceded within the class it self and its children.
- The publisher and the subscriber are decoupled so you can change one without effecting the other.
- Adopting this pattern ensures consistency and makes it more straight forward and manageable.
Happy coding!
Top comments (0)