Skip to content

Multicast DELEGATES and Events in C# – what are they and how do they work?

Lost in coding? Discover our Learning Paths!
Lost in coding? Discover our Learning Paths!

Welcome back to a new C# tutorial. Lets take a look at C# Events and Delegates!

In this tutorial, we will learn about multicast delegates and events!.

Let’s first explore the problem.

Imagine we are developing a video game from scratch; our code is a combination of Graphics and Audio libraries and the logic for our player.

So let’s start coding it : ).

Setting up The Project

Consider the following simple classes.

1. RenderingEngine Class

//RenderingEngine Class
public class RenderingEngine {
    //at the start of the game, we want to enable the rendering engine and start drawing the visuals
    public void StartGame() {
        Console.WriteLine("Rendering Engine Started");
        Console.WriteLine("Drawing Visuals....");
    }

    //when the game is over, we want to stop our rendering engine
    public void GameOver() {
        Console.WriteLine("Rendering Engine Stopped");
    }
}

2. AudioSystem Class

//Audio System Class
public class AudioSystem {
    //at the start of the game, we want to enable the audio system and start playing audio clips
    public void StartGame() {
        Console.WriteLine("Audio System Started...");
        Console.WriteLine("Playing Audio...");
    }
    //when the game is over, we want to stop the audio system
    public void GameOver() {
        Console.WriteLine("Audio System Stopped");
    }
}

3. Player Class

//Player class
public class Player {

    //Player name
    public string PlayerName { get; set; }

    //simple constructor
    public Player(string name) {
        this.PlayerName = name;
    }

    //at the start of the game, spawn the player.
    public void StartGame() {
        Console.WriteLine($"Spawning Player with ID : {PlayerName}");
    }
    //when the game is over, remove the player from the game
    public void GameOver() {
        Console.WriteLine($"Removing Player with ID : {PlayerName}");
    }
}

So now, in the main method, let’s create an audio system object, a rendering engine object, and a couple of players.

static void Main(string[] args) {

    //create an audio system
    AudioSystem audioSystem = new AudioSystem();
    //create a rendering engine 
    RenderingEngine renderingEngine = new RenderingEngine();
    //create two players and give them Id's
    Player player1 = new Player("SteelCow");
    Player player2 = new Player("DoggoSilva");

		//pause
    Console.Read();
}

We need to start our rendering engine, audio system and spawn the players by calling the StartGame method from each class.

static void Main(string[] args) {

    //create an audio system
    AudioSystem audioSystem = new AudioSystem();
    //create a rendering engine 
    RenderingEngine renderingEngine = new RenderingEngine();
    //create two players and give them Id's
    Player player1 = new Player("SteelCow");
    Player player2 = new Player("DoggoSilva");

		//start the audio system and the rendering engine
    audioSystem.StartGame();
    renderingEngine.StartGame();

    //spawn the players
    player1.StartGame();
    player2.StartGame();

    Console.WriteLine("Game is Running....Press any key to end the game.");
  
    Console.Read();
}

Also, we need to shut them down and remove the players at the end of our program by calling GameOver from each class as well!.

static void Main(string[] args) {

    //create an audio system
    AudioSystem audioSystem = new AudioSystem();
    //create a rendering engine 
    RenderingEngine renderingEngine = new RenderingEngine();
    //create two players and give them Id's
    Player player1 = new Player("SteelCow");
    Player player2 = new Player("DoggoSilva");

    //start the audio system and the rendering engine
    audioSystem.StartGame();
    renderingEngine.StartGame();

    //spawn the players
    player1.StartGame();
    player2.StartGame();

    Console.WriteLine("Game is Running....Press any key to end the game.");
	  //pause
		Console.Read();

    //shut down the rendering engine and the audio system
    renderingEngine.GameOver();
    audioSystem.GameOver();

    //remove the players
    player1.GameOver();
    player2.GameOver();

		//pause
    Console.Read();
}

—Run the app—

 

C# Events and Delegates

 

So our application is working correctly but, what if we decided to add more systems or more players?.

We can easily forget to call the StartGame or GameOver, or even worse, call one of the methods twice, which could cause all kinds of problems and require us to place a lot of protection code!.

This is where multicast delegates come in handy : ). A multicast delegate is a delegate that works as a list where it can store a reference to more than one method; it’s as simple as that !.

So let’s create a static class called GameEventManager which will handle our multicast delegates.

public static class GameEventManager {

	
}

And let’s define a new delegate type called GameEvent which will take nothing for its parameters and return nothing.

public static class GameEventManager {

		//a new delegate type called GameEvent
		 public delegate void GameEvent();
		
}

Now let’s create two delegates variables called OnGameStart and OnGameOver, which are the two main events happening on our objects.

public static class GameEventManager {

		//a new delegate type called GameEvent
		 public delegate void GameEvent();
		
		//create two delegates variables called OnGameStart and OnGameOver
		public static GameEvent OnGameStart, OnGameOver;

}

 

Subscribing to our Delegates

 

Back to our 3 Classes, let’s add the StartGame and GameOver methods to the OnGameStart and OnGameOver delegates.

Also, there is no need for these methods to be public so let’s make them private.

The best place to add our methods to the GameEventManager delegates is from within the constructor itself. When an object gets created, its methods will automatically be added to the delegates or, in other words, Subscribe to them.

1. Audio System Class

As we said since a delegate can actually store more than one method at the same time, making them multicast delegates we will use += to add a method to the delegate, not just =.

//Audio System Class
public class AudioSystem {
    //simple constructor
		public AudioSystem() {
				//subscribe to the OnGameStart and OnGameOver events.
        GameEventManager.OnGameStart += StartGame;
        GameEventManager.OnGameOver += GameOver;
    }
    //at the start of the game we want to enable the audio system and start playing audio clips
    private void StartGame() {
        Console.WriteLine("Audio System Started...");
        Console.WriteLine("Playing Audio...");
    }
    //when the game is over we want to stop the audio system
    private void GameOver() {
        Console.WriteLine("Audio System Stopped");
    }
}

2. RenderingEngine Class

//RenderingEngine Class
public class RenderingEngine {
    //simple constructor
    public RenderingEngine() {
        //subscribe to the OnGameStart and OnGameOver events.
        GameEventManager.OnGameStart += StartGame;
        GameEventManager.OnGameOver += GameOver;
    }

    //at the start of the game we want to enable the rendering engine and start drawing the visuals
    private void StartGame() {
        Console.WriteLine("Rendering Engine Started");
        Console.WriteLine("Drawing Visuals....");
    }

    //when the game is over we want to stop our rendering engine
    private void GameOver() {
        Console.WriteLine("Rendering Engine Stopped");
    }
}

3. Player Class

//Player class
public class Player {

    //Player name
    public string PlayerName { get; set; }

    //simple constructor
    public Player(string name) {
        this.PlayerName = name;
        //subscribe to the OnGameStart and OnGameOver events.
        GameEventManager.OnGameStart += StartGame;
        GameEventManager.OnGameOver += GameOver;
    }

    //at the start of the game spawn the player.
    private void StartGame() {
        Console.WriteLine($"Spawning Player with ID : {PlayerName}");
    }
    //when the game is over remove the player from the game
    private void GameOver() {
        Console.WriteLine($"Removing Player with ID : {PlayerName}");
    }

}

Now in our GameEventManager, Let’s add two public static methods to trigger the events from the main methods.

//a static class to handle our game events
public static class GameEventManager {

    //a new delegate type called GameEvent
    public delegate void GameEvent();

    //create two delegates variables called OnGameStart and OnGameOver
    public static event GameEvent OnGameStart, OnGameOver;

    //a static method to trigger OnGameStart
    public static void TriggerGameStart() {
        //check if the OnGameStart event is not empty, meaning that other methods already subscribed to it
        if (OnGameStart != null) {
            //print a simple message
            Console.WriteLine("The game has started....");
            //call the OnGameStart that will trigger all the methods subscribed to this event
            OnGameStart();
        }
    }

    //a static method to trigger OnGameOver
    public static void TriggerGameOver() {
        //check if the OnGameOver event is not empty, meaning that other methods already subscribed to it
        if (OnGameOver != null) {
            //print a simple message
            Console.WriteLine("The game is over...");
            //call the OnGameOver that will trigger all the methods subscribed to this event 
            OnGameOver();
        }
    }
}

Now let’s have a look at the code in our main method. We will see a lot of errors, this is because our StartGame and GameOver methods are no longer public. But it’s fine since we no longer need to call them directly after we made sure they are subscribed to the events in our GameEventManager class.

static void Main(string[] args) {

        //create an audio system
        AudioSystem audioSystem = new AudioSystem();
        //create a rendering engine 
        RenderingEngine renderingEngine = new RenderingEngine();
        //create two players and give them Id's
        Player player1 = new Player("SteelCow");
        Player player2 = new Player("DoggoSilva");

        //trigger the GameStart event
        GameEventManager.TriggerGameStart();

        Console.WriteLine("Game is Running....Press any key to end the game.");
        //pause
        Console.Read();

        //trigger the GameOver event
        GameEventManager.TriggerGameOver();
        
        //pause
        Console.Read();
    }

}

Now let’s add a new player for example and run the application.

static void Main(string[] args) {

      //create an audio system
      AudioSystem audioSystem = new AudioSystem();
      //create a rendering engine 
      RenderingEngine renderingEngine = new RenderingEngine();
      //create two players and give them Id's
      Player player1 = new Player("SteelCow");
      Player player2 = new Player("DoggoSilva");
      Player player3 = new Player("DragonDog");

      //trigger the GameStart event
      GameEventManager.TriggerGameStart();

      Console.WriteLine("Game is Running....Press any key to end the game.");
      //pause
      Console.Read();

      //trigger the GameOver event
      GameEventManager.TriggerGameOver();
      
      //pause
      Console.Read();
  }

}

We can see that our third player got his StartGame and GameOver methods called automatically. Because our objects were notified when both the OnGameStart and OnGameOver events got triggered : )

 

The Problem with Delegates

 

Now when using delegates some things can go wrong.

Let’s first see some of the mistakes that can cost us hours to fix.

Let’s say by mistake we instead of subscribing a method to our events using +=, we used =.

In the Player Class:

Change the += to =.

//Player class
public class Player {

    //Player name
    public string PlayerName { get; set; }

    //simple constructor
    public Player(string name) {
        this.PlayerName = name;
        //subscribe to the OnGameStart and OnGameOver events.
        GameEventManager.OnGameStart = StartGame;
        GameEventManager.OnGameOver = GameOver;
    }

    //at the start of the game spawn the player.
    private void StartGame() {
        Console.WriteLine($"Spawning Player with ID : {PlayerName}");
    }
    //when the game is over remove the player from the game
    private void GameOver() {
        Console.WriteLine($"Removing Player with ID : {PlayerName}");
    }

}

This way we will remove all the methods referenced in the OnGameStart and OnGameOver events, and setting the last Player object that was created as the only subscriber to these events!.

Also since the OnGameStart and OnGameOver events are actually public we can call them directly from anywhere.

In the RenderingEngine Class:

Let’s call the OnGameStart directly from within the constructor.

//RenderingEngine Class
public class RenderingEngine {
    //simple constructor
    public RenderingEngine() {
        //subscribe to the OnGameStart and OnGameOver events.
        GameEventManager.OnGameStart += StartGame;
        GameEventManager.OnGameOver += GameOver;
        GameEventManager.OnGameStart();
    }

    //at the start of the game we want to enable the rendering engine and start drawing the visuals
    private void StartGame() {
        Console.WriteLine("Rendering Engine Started");
        Console.WriteLine("Drawing Visuals....");
    }

    //when the game is over we want to stop our rendering engine
    private void GameOver() {
        Console.WriteLine("Rendering Engine Stopped");
    }
}

Now let’s run the application.

 

C# Events and Delegates

 

It’s all messed up!. Now we can’t mark our events as private because we need them to be public to subscribe to them in our classes constructors.

C# Events

 

Now we’ve been using the name event when mentioning delegates because our delegates were literally behaving like events!. But in fact, there is a special type of delegates called events that is made to prevent these kinds of mistakes.

In The GameEventManager Class:

Mark our delegates as events using the event keyword.

//a static class to handle our game events
public static class GameEventManager {

    //a new delegate type called GameEvent
    public delegate void GameEvent();

    //create two delegates variables called OnGameStart and OnGameOver
    public static event GameEvent OnGameStart, OnGameOver;

    //a static method to trigger OnGameStart
    public static void TriggerGameStart() {
        //check if the OnGameStart event is not empty, meaning that other methods already subscribed to it
        if (OnGameStart != null) {
            //print a simple message
            Console.WriteLine("The game has started....");
            //call the OnGameStart that will trigger 
            OnGameStart();
        }
    }

    //a static method to trigger OnGameOver
    public static void TriggerGameOver() {
        //check if the OnGameStart event is not empty, meaning that other methods already subscribed to it
        if (OnGameOver != null) {
            //print a simple message
            Console.WriteLine("The game is over...");
            //call the OnGameStart that will trigger 
            OnGameOver();
        }
    }
}

You will notice that we have few errors that popped suddenly.

Error 1

In the RenderingEngine Constructor:

 

C# Events and Delegates

 

We can no longer call the event directly since OnGameStart is now an event, we will be only allowed to call it from within the class it was created in.

—Remove the line with the error—

//RenderingEngine Class
public class RenderingEngine {
    //simple constructor
    public RenderingEngine() {
        //subscribe to the OnGameStart and OnGameOver events.
        GameEventManager.OnGameStart += StartGame;
        GameEventManager.OnGameOver += GameOver;
                                                 
    }

    //at the start of the game we want to enable the rendering engine and start drawing the visuals
    private void StartGame() {
        Console.WriteLine("Rendering Engine Started");
        Console.WriteLine("Drawing Visuals....");
    }

    //when the game is over we want to stop our rendering engine
    private void GameOver() {
        Console.WriteLine("Rendering Engine Stopped");
    }
}

Error 2

In the Player class constructor.

 

C# Events and Delegates

 

When a delegate is marked as an event, it is forced to behave like a list, so having an event on the left side of the = is no longer allowed!

—Change the = back to += —

//Player class
public class Player {

    //Player name
    public string PlayerName { get; set; }

    //simple constructor
    public Player(string name) {
        this.PlayerName = name;
        //subscribe to the OnGameStart and OnGameOver events.
        GameEventManager.OnGameStart += StartGame;
        GameEventManager.OnGameOver += GameOver;
    }

    //at the start of the game spawn the player.
    private void StartGame() {
        Console.WriteLine($"Spawning Player with ID : {PlayerName}");
    }
    //when the game is over remove the player from the game
    private void GameOver() {
        Console.WriteLine($"Removing Player with ID : {PlayerName}");
    }

}

Finally, run the app again!

 

C# Events and Delegates

Perfect!

If you want to read more please check out this article C# OnClick Events.

Complete Code for C# Events and Delegates

using System;
using System.Collections.Generic;

namespace DelegatesTestingGround {
    class Program {
      
  static void Main(string[] args) {

      //create an audio system
      AudioSystem audioSystem = new AudioSystem();
      //create a rendering engine 
      RenderingEngine renderingEngine = new RenderingEngine();
      //create two players and give them Id's
      Player player1 = new Player("SteelCow");
      Player player2 = new Player("DoggoSilva");
      Player player3 = new Player("DragonDog");

      //trigger the GameStart event
      GameEventManager.TriggerGameStart();

      Console.WriteLine("Game is Running....Press any key to end the game.");
      //pause
      Console.Read();

      //trigger the GameOver event
      GameEventManager.TriggerGameOver();
      
      //pause
      Console.Read();
  }

}

//a static class to handle our game events
public static class GameEventManager {

  //a new delegate type called GameEvent
  public delegate void GameEvent();

  //create two delegates variables called OnGameStart and OnGameOver
  public static event GameEvent OnGameStart, OnGameOver;

  //a static method to trigger OnGameStart
  public static void TriggerGameStart() {
      //check if the OnGameStart event is not empty, meaning that other methods already subscribed to it
      if (OnGameStart != null) {
          //print a simple message
          Console.WriteLine("The game has started....");
          //call the OnGameStart that will trigger 
          OnGameStart();
      }
  }

  //a static method to trigger OnGameOver
  public static void TriggerGameOver() {
      //check if the OnGameStart event is not empty, meaning that other methods already subscribed to it
      if (OnGameOver != null) {
          //print a simple message
          Console.WriteLine("The game is over...");
          //call the OnGameStart that will trigger 
          OnGameOver();
      }
  }
}

//Audio System Class
public class AudioSystem {
  //simple constructor
  public AudioSystem() {
      //subscribe to the OnGameStart and OnGameOver events.
      GameEventManager.OnGameStart += StartGame;
      GameEventManager.OnGameOver += GameOver;
  }
  //at the start of the game we want to enable the audio system and start playing audio clips
  private void StartGame() {
      Console.WriteLine("Audio System Started...");
      Console.WriteLine("Playing Audio...");
  }
  //when the game is over we want to stop the audio system
  private void GameOver() {
      Console.WriteLine("Audio System Stopped");
  }
}

//RenderingEngine Class
public class RenderingEngine {
  //simple constructor
  public RenderingEngine() {
      //subscribe to the OnGameStart and OnGameOver events.
      GameEventManager.OnGameStart += StartGame;
      GameEventManager.OnGameOver += GameOver;
  }

  //at the start of the game we want to enable the rendering engine and start drawing the visuals
  private void StartGame() {
      Console.WriteLine("Rendering Engine Started");
      Console.WriteLine("Drawing Visuals....");
  }

  //when the game is over we want to stop our rendering engine
  private void GameOver() {
      Console.WriteLine("Rendering Engine Stopped");
  }
}

//Player class
public class Player {

  //Player name
  public string PlayerName { get; set; }

  //simple constructor
  public Player(string name) {
      this.PlayerName = name;
      //subscribe to the OnGameStart and OnGameOver events.
      GameEventManager.OnGameStart += StartGame;
      GameEventManager.OnGameOver += GameOver;
  }

  //at the start of the game spawn the player.
  private void StartGame() {
      Console.WriteLine($"Spawning Player with ID : {PlayerName}");
  }
  //when the game is over remove the player from the game
  private void GameOver() {
      Console.WriteLine($"Removing Player with ID : {PlayerName}");
  }

}

}
Lost in coding? Discover our Learning Paths!
Lost in coding? Discover our Learning Paths!
Enter your email and we will send you the PDF guide:
Enter your email and we will send you the PDF guide