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

5.5 KiB

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.