Delegates, events en call backs in C#

  1. Inleiding
  2. Delegate
  3. Anonieme functie
  4. Event
  5. Slot

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.

up | down

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.

  1. 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);

  2. 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… };

  3. We roepen de onderhoudsbeurt methode aan en we geven als parameters een telefoonnummer en delegate functie informeerMij mee als parameters:
    onderhoudsBeurt(123, informeerMij);

  4. Methode onderhoudsbeurt heeft deze parameters:
    public static void onderhoudsBeurt( int telefoonNummer,  delegatie doeWatDeKlantWil) { … }

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

up | down

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

up | down

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


up | down

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…

up


Laat een reactie achter

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *