finish lecture 11, fix theme not being applied in cli html conversion

- update readme
main
borb 2 weeks ago
parent 0f0335833e
commit 88c8c953c6

@ -5,7 +5,7 @@
{
"match": "\\.md$",
"notMatch": "README\\.md$",
"cmd": "marp \"${fileDirname}\\${fileBasename}\" -o \"${fileDirname}\\${fileBasenameNoExt}-slides.html\" --html true",
"cmd": "marp \"${fileDirname}\\${fileBasename}\" -o \"${fileDirname}\\${fileBasenameNoExt}-slides.html\" --html true --theme \"${fileDirname}\\.themes\\buutti.css\"",
"useShortcut": false,
"silent": false
},

File diff suppressed because one or more lines are too long

@ -1,289 +1,448 @@
# Delegates and Events
![](imgs/11%20Delegates%20and%20Events_0.png)
---
marp: true
paginate: true
math: mathjax
theme: buutti
title: N. Delegates and events
---
# Overview
Delegates
# Delegates and events
Multicast Delegates
<!-- headingDivider: 5 -->
<!-- class: invert -->
Anonymous Methods
## Overview
Events
* Delegates
* Multicast Delegates
* Anonymous Methods
* Events
# Delegates
## Delegates
* __Delegates __ are reference type variables that hold a __reference to a method__ or multiple methods
* *__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:
### Creating a delegate
delegate \<return type> \<delegate name>(\<parameters>);
Example:
* 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!
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:
### 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 multicast delegates
Using the same PrintDelegate delegate as before, we could do this:
* 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)
# Using Multicast Delegates (continued)
</div>
</div>
Methods can be removed from the delegate with the "-" operator:
### 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 - 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:
### 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 (continued)
---
Now we can create a new DelegateTest object and pass the delegate to the object constructor:
<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)
# Anonymous Methods
</div>
</div>
Delegates can be initialized anonymously (without a specified name)
Anonymous method in variable declaration:
### 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!
Notice that the actual method that prints the text is not declared anywhere
You can use an empty anonymous method to initialize a delegate which does nothing:
---
* 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);
}
}
```
static void Main(string[] args)
### 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);
}
}
```
// Initialize an empty delegate, add method later...
</div>
<div markdown='1'>
SomeDelegate myDelegate = new SomeDelegate(delegate { });
```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);
}
}
```
# Events
</div>
<div markdown='1'>
* 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
---
```csharp {7-8}
class Program
{
static void Main()
{
var pub = new Publisher();
Tight coupling
pub.SampleEvent +=
() => Console.WriteLine("Sample event!");
# Events (continued)
pub.OnSampleEvent();
}
}
```
Events are signals that are raised by a __Publisher __ and received by a __Subscriber__
</div>
</div>
The publisher does not know or care what, if any, object receives the signal
Changes in the subscriber classes do not affect the publisher
### Custom event handler
# Events - Example
* 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);
Events are created in two steps:
// Declare the event.
public event SampleEventHandler SampleEvent;
Declare a delegate
protected virtual void OnSampleEvent()
{
SampleEvent?.Invoke(this);
}
}
```
Declare a variable of the delegate with the event keyword
### Event arguments
public delegate void GameOver();
* 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
}
class Game
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"));
}
}
```
public event GameOver GameOverEventHandler;
// Some game logic here...
### "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;
# Exercise 1: A Rudimentary Event System
game.GameOver += gameOverSound.Play;
game.GameOver += gameOverScreen.Show;
}
}
```
Create a console application for controlling a plant treatment system, that has three methods which print the following outputs:
</div>
<div markdown='1'>
| void ReleaseWater() | Releasing water... |
| :-: | :-: |
| void ReleaseFertilizer() | Releasing fertilizer... |
| void IncreaseTemperature() | Increasing temperature... |
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
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)
</div>
</div>
If the user types run, all the methods that are on (= added to the delegate), will be executed.
## Exercise 1: A rudimentary event system
<!--_class: "exercise invert" -->
![](imgs/11%20Delegates%20and%20Events_3.png)
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)

@ -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) | 🌕 | 🌕 |

Loading…
Cancel
Save