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.
290 lines
5.5 KiB
Markdown
290 lines
5.5 KiB
Markdown
# Delegates and Events
|
|
|
|

|
|
|
|
---
|
|
|
|
# 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");
|
|
|
|
}
|
|
|
|

|
|
|
|
# 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);
|
|
|
|

|
|
|
|
# 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.
|
|
|
|

|
|
|