Delegates, events en call backs in C#
Inleiding
Auto’s moeten wel eens naar de garage voor een onderhoudsbeurt en meestal heb je geen zin om constant achter dat onderhoudsbedrijf aan te moeten zitten om te vragen of het gevraagde gedaan is.
Je zou graag willen dat de garage op eigen initiatief aan jou laat weten dat de onderhoudsbeurt is gedaan. Bijvoorbeeld dat je door één van de lieftallige dames op de uitgelichte afbeelding wordt gebeld met de boodschap dat de auto klaar is. Je kan aan de hand daarvan je vervolgacties doen (met het openbaar vervoer naar de garage gaan om de auto op te halen).
Het is met software niet anders. Als je een ander programma hebt opgestart dan wil je niet constant kijken of dat programma klaar is. Je wil van dat programma dat je hebt opgestart een sein krijgen dat het klaar is zodat je eigen programma’s naar aanleiding daarvan automatisch gestart worden. Het is in C# allemaal mogelijk met delegates en events en we zullen het één en ander in deze post demonstreren.
Delegate
Een delegate is in feite een soort functiepointer. Je koppelt een programma aan die functiepointer en via die functiepointer start je een programma op. Je start het programma dus niet meer direct op, maar via die functiepointer. We zullen het delegate concept toelichten aan de hand van een garagebedrijf dat onderhoudsbeurten doet.
Klanten brengen hun auto’s voor een onderhoudsbeurt naar de garage en de klanten worden geïnformeerd zodra de onderhoudsbeurt is gedaan. Elke klant wil anders geïnformeerd worden. Voor de ene klant volstaat een telefoontje, maar andere klanten willen weer een WhatsApp en dan zijn er ook nog de klanten van de oude stempel die gefaxt willen worden (een fax? bestaat dat nog ?).
We willen de service bieden die de klant wil en we definiëren een delegate. Aan de delegate koppelen we een functie en wat voor functie? Dat kan per klant verschillen want de ene klant wil gebeld worden en de andere gefaxt en een andere klant wil weer via een ander medium geïnformeerd worden.
- We gebruiken in dit voorbeeld een hele simpele signatuur voor de delegate. Er is alleen input zijnde een int en verder niks:
public delegate void delegatie (int telefoonNummer); - We koppelen aan de delegate als volgt een functie waarin de klant aangeeft dat naar een bepaald telefoonnummer gebeld mag worden zodra de onderhoudsbeurt is gedaan:
delegatie informeerMij = delegate (int telefoonNummer) { … de klant wil gebeld worden op telefoonNummer… }; - We roepen de onderhoudsbeurt methode aan en we geven als parameters een telefoonnummer en delegate functie informeerMij mee als parameters:
onderhoudsBeurt(123, informeerMij); - Methode onderhoudsbeurt heeft deze parameters:
public static void onderhoudsBeurt( int telefoonNummer, delegatie doeWatDeKlantWil) { … } - En in methode onderhoudsbeurt starten we na voltooiing van het eigen werk delegate functie doeWatDeKlantWil op. Aan delegate functie doeWatDeKlantWil is delegate functie informeerMij gekoppeld en informeerMij zal uiteindelijk opstart worden.
namespace Delegaties
{
class MainClass
{
//---------------------------------------
// de delegate
public delegate
void delegatie(int telefoonNummer);
//---------------------------------------
// de functie die gebruik maakt van de delegate
public static void onderhoudsBeurt(
int telefoonNummer,
delegatie doeWatDeKlantWil)
{
Console.WriteLine("Garage:
De onderhoudsbeurt
is gedaan.\r\n");
doeWatDeKlantWil(telefoonNummer);
Console.WriteLine("Garage: De klant is
op de hoogte
gebracht.\r\n");
}
// Main
public static void Main(string[] args)
{
/*
* ----------------------------------
* Als de onderhoudsbeurt is gedaan
* dan wil ik dat als volgt weten:
* ---------------------------------
*/
delegatie
informeerMij = delegate (int telefoonNummer)
{
Console.WriteLine(" ------------------");
Console.WriteLine(" Klant: Bel mij
op dit nummer: {0}",
telefoonNummer);
Console.WriteLine(" ------------------\r\n");
};
// De klant brengt de auto weg
// voor een onderhoudsbeurt
Console.WriteLine("Klant: de auto is
weggebracht.\r\n");
// De garage gaat de onderhoudsbeurt doen
onderhoudsBeurt(123, informeerMij );
// De klant haalt de auto weer op
Console.WriteLine("Klant: de auto is opgehaald.");
}
}
}
En we krijgen dit resultaat, de klant krijgt een belletje:
Klant: de auto is weggebracht.
Garage: De onderhoudsbeurt is gedaan.
---------------------------------
Klant: Bel mij op dit nummer: 123
---------------------------------
Garage: De klant is op de hoogte gebracht.
Press any key to continue...
Anonieme functie
In het vorige voorbeeld hadden we functie informeerMij gedefinieerd en die functie is een delegate type:
delegatie informeerMij = delegate (int telefoonNummer) { … de klant wil gebeld worden op telefoonNummer … };
de delegate functie geven we door aan methode onderhoudsBeurt(…, informeerMij).
We kunnen de hele code ook doorgeven als parameter zonder er een delegate functie voor te definiëren. De hele code is dan een anonieme functie:
onderhoudsBeurt(…, delegate (int telefoonNummer) { … de klant wil gefaxt worden … });
namespace Delegaties
{
class MainClass
{
//---------------------------------------
// de delegate
public delegate
void delegatie(int telefoonNummer);
//---------------------------------------
// de functie die gebruik maakt van de delegate
public static void onderhoudsBeurt(
int telefoonNummer,
delegatie doeWatDeKlantWil)
{
Console.WriteLine("Garage:
De onderhoudsbeurt
is gedaan.\r\n");
doeWatDeKlantWil(telefoonNummer);
Console.WriteLine("Garage: De klant is
op de hoogte
gebracht.\r\n");
}
// Main
public static void Main(string[] args)
{
// De klant brengt de auto weg
// voor een onderhoudsbeurt
Console.WriteLine("Klant: de auto is
weggebracht.\r\n");
// Anonieme functie
onderhoudsBeurt(456,
delegate (int telefoonNummer)
{
Console.WriteLine(" --------------------");
Console.WriteLine(" Klant: Stuur een fax
naar dit nummer: {0}",
telefoonNummer);
Console.WriteLine(" --------------------\r\n");
}
);
// De klant haalt de auto weer op
Console.WriteLine("Klant: de auto is opgehaald.");
}
}
}
Gelukkig vonden we nog een oud fax apparaat in de kelder waarmee een fax naar de klant gestuurd kan worden:
Klant: de auto is weggebracht.
Garage: De onderhoudsbeurt is gedaan.
-----------------------------------------
Klant: Stuur een fax naar dit nummer: 456
-----------------------------------------
Garage: De klant is op de hoogte gebracht.
Klant: de auto is opgehaald.
Press any key to continue...
Events
Een programma kan een event opwekken en dat event kan door een ander programma (een event handler) gedetecteerd worden. Het is uiteindelijk de bedoeling dat dat andere programma n.a.v. het event iets gaat doen en daarvoor heb je ook een delegate nodig.
Toegespitst op de situatie dat we onze auto naar de garage brengen. We detecteren het signaal van de garage dat onze auto klaar is. Het signaal van de garage moet ons weer triggeren om bepaalde dingen te doen. In dit voorbeeld het pakken van de bus die ons naar de garage brengt zodat we de auto kunnen ophalen en ermee naar huis kunnen gaan:
using System;
namespace Werkplaats
{
class Publisher
{
public delegate void InformeerDeKlant();
public
event InformeerDeKlant informeerDeKlantEvent;
public delegate void DoeWatDeKlantWil();
public void Onderhoudsbeurt(
string klant,
DoeWatDeKlantWil doeWatDeKlantWil)
{
// De onderhoudsbeurt
Console.WriteLine("Begin onderhoudsbeurt");
Console.WriteLine(
klant +
" heeft de auto gebracht.");
Console.WriteLine(
"De monteur begint met
de onderhoudsbeurt.");
Console.WriteLine(
"De monteur is klaar met
de onderhoudsbeurt.");
Console.WriteLine(
"Einde onderhoudsbeurt\r\n");
// Doe wat de klant wil
Console.WriteLine(
"Begin informeren klant");
Console.Write(
"Benader " + klant +
" zoals aangegeven:\r\n");
doeWatDeKlantWil();
Console.WriteLine("Einde informeren
klant\r\n");
// Stel de klant in de gelegenheid
// vervolgacties te doen
if (informeerDeKlantEvent != null)
{
informeerDeKlantEvent();
}
}
}
class Subscriber
{
// methode
public static void pakDeBus()
{
Console.WriteLine("Begin KlantEvent");
Console.WriteLine(" De garage heeft laten weten
dat de auto opgehaald
kan worden.");
Console.WriteLine(" We pakken de bus die naar
de garage gaat.");
Console.WriteLine("Einde KlantEvent");
}
public static void Main(string[] args)
{
// Instantieer
Publisher garage = new Publisher();
// als de garage klaar is dan moet
// van onze kant iets gebeuren
garage.informeerDeKlantEvent += pakDeBus;
// Begin met de onderhoudsbeurt
garage.Onderhoudsbeurt(
"Mevrouw Jansen",
delegate ()
{
Console.WriteLine(" ---");
Console.WriteLine(" Bel naar nummer: 123456");
Console.WriteLine(" ---");
});
}
}
}
Het “signaal” dat uitgaat van de garage is een delegate en een event:
public delegate void InformeerDeKlant();
public event InformeerDeKlant informeerDeKlantEvent;
En de klant hangt aan het signaal van de garage een eigen actie:
garage.informeerDeKlantEvent += pakDeBus;
hetgeen als resultaat heeft dat we een bus pakken richting de garage.
Begin onderhoudsbeurt
Mevrouw Jansen heeft de auto gebracht.
De monteur begint met de onderhoudsbeurt.
De monteur is klaar met de onderhoudsbeurt.
Einde onderhoudsbeurt
Begin informeren klant
Benader Mevrouw Jansen zoals aangegeven:
---
Bel naar nummer: 123456
---
Einde informeren klant
Begin KlantEvent
De garage heeft laten weten dat
de auto opgehaald kan worden.
We pakken de bus die naar de garage gaat.
Einde KlantEvent
Press any key to continue...
Slot
In deze post heb ik laten zien wat je met delegates kunt doen. Een programma kan via delegates extra dingen doen (de call back) na voltooiing van haar eigen werk. Extra dingen die het aanroepende programma aandraagt en waarvan het graag wil dat die ook gedaan worden. We hebben het geïllustreerd met het voorbeeld van een garagebedrijf dat na het doen van de onderhoudsbeurt de klant informeert op een manier zoals door de klant is aangegeven.
Het is ook mogelijk dat een programma een signaal afgeeft (een event) zodra het programma klaar is met draaien. Het aanroepende programma kan n.a.v. dat signaal haar eigen programma’s opstarten. Ook dit voorbeeld hebben we geïllustreerd met het voorbeeld van een garagebedrijf waarbij het garagebedrijf een signaal laat doen uitgaan nadat de onderhoudsbeurt is gedaan en de klant n.a.v. dat signaal de bus pakt richting de garage. We hebben gebruik gemaakt van delegates en events om het één en ander mogelijk te maken.
Hopelijk ben je met deze posting weer wat wijzer geworden en ik hoop je weer terug te zien in één van mijn volgende blog posts. Wil je weten wat ik nog meer over C# heb geschreven? Hit the C# button…