You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
csharp-basics/11. Delegates and Events.md

290 lines
5.5 KiB
Markdown

# Delegates and Events
![](imgs/11%20Delegates%20and%20Events_0.png)
---
# Overview
Delegates
Multicast Delegates
Anonymous Methods
Events
# Delegates
* __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:
delegate \<return type> \<delegate name>(\<parameters>);
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");
}
![](imgs/11%20Delegates%20and%20Events_1.png)
# 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"
...
}
# 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:
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);
![](imgs/11%20Delegates%20and%20Events_2.png)
# 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
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 which does nothing:
delegate void SomeDelegate();
class Program
{
static void Main(string[] args)
{
// Initialize an empty delegate, add method later...
SomeDelegate myDelegate = new SomeDelegate(delegate { });
}
}
# 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)
Events are signals that are raised by a __Publisher __ and received by a __Subscriber__
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
{
public event GameOver GameOverEventHandler;
// Some game logic here...
// ...
}
# 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... |
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.
![](imgs/11%20Delegates%20and%20Events_3.png)