---
marp: true
paginate: true
math: mathjax
theme: buutti
title: N. Delegates and events
---

# Delegates and events

<!-- headingDivider: 5 -->
<!-- class: invert -->

## Overview

* Delegates
  * Multicast Delegates
  * Anonymous Methods
* Events

## Delegates

* [Delegates](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/) are reference type variables that hold a *__reference to a method__* or multiple methods
  * Class objects hold a reference to a class instance
  * Delegate objects hold a reference to a method / methods
* Similar to function pointers in C and C++, or how any function in JavaScript works
* Allows for methods to be passed as variables, useful for creating, for example, events

### Creating a delegate

* Declare a delegate using the following syntax:
  ```csharp
  delegate returnType DelegateName(parameters);
  ```
* Example:

  ```csharp
  delegate void PrintDelegate(string output);
  ```
* This creates a new delegate of type `void`, named `PrintDelegate` and one parameter of type `string`
* The referenced method return and parameter types have to match the delegate!

### Referencing a delegate

* After creating the delegate, it can be instantiated and the method assigned to it with the method name:
  ```csharp
  delegate void PrintDelegate(string output);

  static void Main(string[] args)
  {
    void PrintInLower(string text)
    {
      Console.WriteLine(text.ToLower());
    }

    PrintDelegate print = PrintInLower;
    print("AaaBbbCcc");     // Outputs "aaabbbccc"
  }
  ```

### Using multicast delegates

* Delegates can be composed of multiple methods using the `+` operator
* Using the same `PrintDelegate` delegate as before, we could do this:
<div class='columns21' markdown='1'>
<div markdown='1'>

  ```csharp
  delegate void PrintDelegate(string output);
  static void Main(string[] args)
  {
    void PrintInLower(string text)
    {
      Console.WriteLine(text.ToLower());
    }
    void PrintInUpper(string text)
    {
      Console.WriteLine(text.ToUpper());
    }
    PrintDelegate print = PrintInLower;
    print += PrintInUpper;
    print("AaaBbbCcc");
  }
  ```

</div>
<div markdown='1'>

![](imgs/11%20Delegates%20and%20Events_1.png)

</div>
</div>

### Removing methods

* Methods can be removed from the delegate with the `-` operator:
  ```csharp
  delegate void PrintDelegate(string output);
  static void Main(string[] args)
  {
    ...
    print -= PrintInLower;
    print("AaaBbbCcc");     // Outputs "AAABBBCCC"
    ...
  }
  ```

### Delegates: An example

* Let's extend our previous example (without the `-=` part) by creating a new class called `DelegateTest`, and giving it a constructor that takes a `PrintDelegate` object as a parameter:
  ```csharp
  public class DelegateTest
  {
    public DelegateTest(PrintDelegate printDelegate)
    {
      printDelegate("This Method Was Called From Another Class!");
    }
  }
  ```

---

<div class='columns21' markdown='1'>
<div markdown='1'>

```csharp
void PrintInLower(string text)
{
  Console.WriteLine(text.ToLower());
}
void PrintInUpper(string text)
{
  Console.WriteLine(text.ToUpper());
}
// Initialize new delegate which is composed of PrintInLower method
PrintDelegate print = PrintInLower;

// Add PrintInUpper method to the delegate
print += PrintInUpper;

// Send the delegate to the class constructor
DelegateTest delegateTest = new DelegateTest(print);
```

</div>
<div markdown='1'>

Now we can create a new DelegateTest object and pass the delegate to the object constructor:

![](imgs/11%20Delegates%20and%20Events_2.png)

</div>
</div>


### Anonymous methods

* Delegates can be initialized anonymously (without a specified name)
* Anonymous method in variable declaration:
  ```csharp
  delegate void PrintDelegate(string output);
  bool printUpper = true;
  PrintDelegate printCheckUpper =
    delegate (string text)
    {
      if (printUpper)
        Console.WriteLine(text.ToUpper());
      else
        Console.WriteLine(text);
    };

  printCheckUpper("I'm not angry!");        // Outputs I'M NOT ANGRY!
  ```
* Notice that the actual method that prints the text is not declared anywhere!

---

* You can use an empty anonymous method to initialize a delegate that does nothing:
  ```csharp
  delegate void SomeDelegate();

  class Program
  {
    static void Main(string[] args)
    {
      // Initialize an empty delegate, add method later...
      SomeDelegate myDelegate = new SomeDelegate(delegate { });
    }
  }
  ```

## Events

### The problem

<div class='columns' markdown='1'>
<div markdown='1'>

  ```csharp
  class Game
  {
    Sound gameOverSound;
    Window gameOverScreen;

    void OnGameOver()
    {
      gameOverSound.Play();   // plays some sound
      gameOverScreen.Show();  // shows a screen
    }
  }
  ...
  class Program
  {
    static void Main()
    {
      var game = new Game();

      // somewhere in the game logic...
      // game.OnGameOver();
    }
  }
  ```
</div>
<div markdown='1'>

* Consider a game engine with three classes, `Sound`, `Window` and `Game`.
* If implemented like this, the `Game` class has to know about the `Sound` and `Window` classes
* $\Rightarrow$ `Game` is ***tightly coupled***, and thus ***dependent*** on the `Sound` and `Window` classes
* Changes in either of the classes could break the code!

</div>
</div>

### The solution: Events

* A solution to this problem is the [Publisher-subscriber pattern](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern)
  * In C#, this pattern is implemented with the [`event` keyword](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/event?redirectedfrom=MSDN)
* Events are ***signals*** that are raised by a  *__Publisher__* and received by a *__Subscriber__*
  * The publisher does not know or care who, if anyone, receives the signal
  * Changes in the subscriber classes do not affect the publisher
* In C#, events are multicast delegates
* When an object triggers an event, the event invokes ***event handlers***
  * Event handlers are delegate instances added to the event

### Raising events

* Events consist of two elements:
  * A delegate that identifies the method that provides the response to the event
    * In the simplest case, we can use the built-in [`EventHandler`](https://learn.microsoft.com/en-us/dotnet/api/system.eventhandler?view=net-9.0) delegate 
  * An optional class to hold event data if the event provides data.
  ```csharp
  public class Publisher
  {
      public event EventHandler SampleEvent;
      // Wrap the event in a protected virtual method
      // to enable derived classes to raise the event.
      protected virtual void OnSampleEvent()
      {
          // Raise the event in a thread-safe manner using the ?. operator.
          SampleEvent?.Invoke(this);
      }
  }
  ```

### Raising the event

Events can only be invoked (i.e., raised) from within the class (or derived classes) or struct where they're declared (the publisher class)

<div class='columns' markdown='1'>
<div markdown='1'>

```csharp
public class Publisher
{
    public event EventHandler SampleEvent;
    protected virtual void OnSampleEvent()
    {
        SampleEvent?.Invoke(this);
    }
}
```

</div>
<div markdown='1'>

```csharp
class Program
{
  static void Main()
  {
    var pub = new Publisher();

    pub.OnSampleEvent();  // ✅ Works fine
    // pub.SampleEvent();    // ❌ Not allowed!
  }
}
```

</div>
</div>

### Adding subscribers to the event

* Now that we know how to raise the event, we can add subscribers to it
* i.e., functions that get called when the event is raised

<div class='columns' markdown='1'>
<div markdown='1'>

```csharp
public class Publisher
{
    public event EventHandler SampleEvent;
    protected virtual void OnSampleEvent()
    {
        SampleEvent?.Invoke(this);
    }
}
```

</div>
<div markdown='1'>

```csharp {7-8}
class Program
{
  static void Main()
  {
    var pub = new Publisher();

    pub.SampleEvent +=
      () => Console.WriteLine("Sample event!");
    
    pub.OnSampleEvent();
  }
}
```

</div>
</div>


### Custom event handler

* We can define the delegate ourselves, for example if we want to send some data to the event.
  ```csharp
  public class Publisher
  {
      public delegate void SampleEventHandler(object sender);

      // Declare the event.
      public event SampleEventHandler SampleEvent;

      protected virtual void OnSampleEvent()
      {
          SampleEvent?.Invoke(this);
      }
  }
  ```

### Event arguments

* Finally, we can send arguments to the event like this:
  ```csharp
  public class SampleEventArgs
  {
      public SampleEventArgs(string text) { Text = text; }
      public string Text { get; } // readonly
  }

  public class Publisher
  {
      public delegate void SampleEventHandler(object sender, SampleEventArgs e);

      public event SampleEventHandler SampleEvent;

      protected virtual void RaiseSampleEvent()
      {
          SampleEvent?.Invoke(this, new SampleEventArgs("Hello"));
      }
  }
  ```


### "Fixing" the Game Over example

<div class='columns' markdown='1'>
<div markdown='1'>

  ```csharp
  public delegate void GameOverHandler();
  class Game
  {
    public event GameOverHandler GameOver;

    protected virtual void OnGameOver()
    {
        GameOver?.Invoke(this);
    }
  }
  ...
  class Program
  {
    static void Main()
    {
      var game = new Game();
      Sound gameOverSound;
      Window gameOverScreen;

      game.GameOver += gameOverSound.Play;
      game.GameOver += gameOverScreen.Show;
    }
  }
  ```

</div>
<div markdown='1'>

1) Declare an event handler delegate (`GameOverHandler`)
2) Declare an instance of the handler with the `event` keyword (`GameOVer`)
3) Declare a virtual method that invokes the event
4) Add methods that subscribe to the event

</div>
</div>

## Exercise 1: A rudimentary event system
<!--_class: "exercise invert" -->

Create a console application for controlling a plant treatment system with three methods that print the following outputs:

| Method | Output |
| :-- | :-- |
| `void ReleaseWater()` | `Releasing water...` |
| `void ReleaseFertilizer()` | `Releasing fertilizer...` |
| `void IncreaseTemperature()` | `Increasing temperature...` |

* All methods are off by default. Create a main loop where the user can...
  * ...type the name of the method to switch each method on (add it to the delegate)
  * ...type `run` to execute all the methods that are on

---

<!--_class: "exercise invert" -->

* ***Hint:*** you can just use switch-case for defining which method should be added to the delegate

* Here's an example console input & output:
  ![](imgs/11%20Delegates%20and%20Events_3.png)
