);
-
-Example:
-
-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:
-
-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:
-
-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");
-
-}
+### 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:
+
+
+
+ ```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");
+ }
+ ```
+
+
+

-# Using Multicast Delegates (continued)
-
-Methods can be removed from the delegate with the "-" operator:
-
-delegate void PrintDelegate(string output);
-
-static void Main(string[] args)
-
-{
-
-...
-
-print -= PrintInLower;
-
-print("AaaBbbCcc"); // Outputs "AAABBBCCC"
-
-...
-
-}
+
+
+
+### 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!");
+ }
+ }
+ ```
-# Delegates - 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:
-
-public class DelegateTest
-
-{
-
-public DelegateTest(PrintDelegate printDelegate)
-
-{
-
-printDelegate("This Method Was Called From Another Class!");
-
-}
-
-}
-
-# Delegates - Example (continued)
+---
-Now we can create a new DelegateTest object and pass the delegate to the object constructor:
+
+
+```csharp
void PrintInLower(string text)
-
{
-
-Console.WriteLine(text.ToLower());
-
+ Console.WriteLine(text.ToLower());
}
-
void PrintInUpper(string text)
-
{
-
-Console.WriteLine(text.ToUpper());
-
+ 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);
+```
-
-
-# Anonymous Methods
-
-Delegates can be initialized anonymously (without a specified name)
-
-Anonymous method in variable declaration:
-
-delegate void PrintDelegate(string output);
-
-bool printUpper = true;
-
-PrintDelegate printCheckUpper =
+
+
-delegate (string text)
-
-{
-
-if (printUpper)
-
-Console.WriteLine(text.ToUpper());
-
-else
+Now we can create a new DelegateTest object and pass the delegate to the object constructor:
-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
+### Anonymous methods
-You can use an empty anonymous method to initialize a delegate which does nothing:
+* 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);
+ };
-delegate void SomeDelegate();
+ printCheckUpper("I'm not angry!"); // Outputs I'M NOT ANGRY!
+ ```
+* Notice that the actual method that prints the text is not declared anywhere!
-class Program
+---
+* 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
+
+
+
+
+ ```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();
+ }
+ }
+ ```
+
+
+
+* 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!
+
+
+
+
+### 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)
+
+
+
+
+```csharp
+public class Publisher
{
+ public event EventHandler SampleEvent;
+ protected virtual void OnSampleEvent()
+ {
+ SampleEvent?.Invoke(this);
+ }
+}
+```
-static void Main(string[] args)
+
+
+```csharp
+class Program
{
+ static void Main()
+ {
+ var pub = new Publisher();
-// Initialize an empty delegate, add method later...
-
-SomeDelegate myDelegate = new SomeDelegate(delegate { });
-
+ pub.OnSampleEvent(); // ✅ Works fine
+ // pub.SampleEvent(); // ❌ Not allowed!
+ }
}
+```
-}
-
-# Events
-
-* Consider the following Game class:
-* class Game
-* {
-* Sound gameOverSound;
-* Window gameOverScreen;
-* // Some game logic here...
-* // ...
-* void OnGameOver()
-* {
-* gameOverSound.Play(); // This plays some sound
-* gameOverScreen.Show(); // This shows some screen
-* }
-* }
-* This raises a couple of problems:
- * The Game class has to know about the Sound and Window classes
- * Changes in either of the classes could break the code
- * = Game is dependent of Sound and Window classes
----
-
-Tight coupling
+
+
-# Events (continued)
+### Adding subscribers to the event
-Events are signals that are raised by a __Publisher __ and received by a __Subscriber__
+* 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
-The publisher does not know or care what, if any, object receives the signal
-
-Changes in the subscriber classes do not affect the publisher
-
-# Events - Example
-
-Events are created in two steps:
-
-Declare a delegate
-
-Declare a variable of the delegate with the event keyword
-
-public delegate void GameOver();
-
-class Game
+
+
+```csharp
+public class Publisher
{
-
-public event GameOver GameOverEventHandler;
-
-// Some game logic here...
-
-// ...
-
+ public event EventHandler SampleEvent;
+ protected virtual void OnSampleEvent()
+ {
+ SampleEvent?.Invoke(this);
+ }
}
+```
-# Exercise 1: A Rudimentary Event System
-
-Create a console application for controlling a plant treatment system, that has three methods which print the following outputs:
+
+
-| void ReleaseWater() | Releasing water... |
-| :-: | :-: |
-| void ReleaseFertilizer() | Releasing fertilizer... |
-| void IncreaseTemperature() | Increasing temperature... |
+```csharp {7-8}
+class Program
+{
+ static void Main()
+ {
+ var pub = new Publisher();
+
+ pub.SampleEvent +=
+ () => Console.WriteLine("Sample event!");
+
+ pub.OnSampleEvent();
+ }
+}
+```
+
+
+
+
+
+### 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
+
+
+
+
+ ```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;
+ }
+ }
+ ```
+
+
+
+
+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
+
+
+
+
+## Exercise 1: A rudimentary event system
+
+
+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
-Create a main loop where the user can switch each method on (= add to a delegate) by writing its name. All methods are off by default. (Hint: you can just use switch-case for defining which method should be added to the delegate)
+---
-If the user types run, all the methods that are on (= added to the delegate), will be executed.
+
-
+* ***Hint:*** you can just use switch-case for defining which method should be added to the delegate
+* Here's an example console input & output:
+ 
diff --git a/README.md b/README.md
index ff39820..c032995 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,7 @@ Material completion denoted with 🌑🌘🌗🌖🌕 .
| # | Lecture | Materials | Exercises |
|---:|------------------------------------------------------------------------|----------:|----------:|
+| 11 | [Delegates and Events](11-delegates-and-events.md) | 🌕 | 🌗 |
| 12 | [Files and Streams](12-files-and-streams.md) | 🌕 | 🌕 |
| 13 | [Generics, IEnumberable and LINQ](13-generics-ienumerable-and-linq.md) | 🌕 | 🌕 |
| 14 | [Exceptions, Threads and Tasks](14-exceptions-threads-and-tasks.md) | 🌕 | 🌕 |