[WikiDyd] [TitleIndex] [WordIndex

(opracował: Robert Szmurło, http://www.iem.pw.edu.pl/~szmurlor)

Komunikacja między obiektami z wykorzystaniem Delegacji

Delegacja jest specjalną konstrukcją w języku C#, która w sposób sformalizowany oraz zapewniający bezpieczeństwo typów na etapie kompilacji pozwala komunikować się między obiektami. Delegacja jest specjalną klasą, która w ukryty sposób przechowuje informacje o:

Delegacja przechowuje powyższe informacje w sposób ukryty, co oznacza, że nie jest to widoczne bezpośrednio dla programisty. Co więcej, programista zazwyczaj nie używa właściwości delegacji związanych z tym, że jest ona klasą. Jedną z ważniejszych cech delegacji jest to, że aby można jej było użyć trzeba ją utworzyć za pomocą operatora  new  , podobnie jak inne normalne klasy.

Poniższy przykład bazuje na założeniu, że mamy do stworzenia pewien system monitoringu stanu rzek. Każda rzeka ma swoje własne parametry określające maksymalny dopuszczalny poziom oraz informacje o aktualnym poziomie. Uaktualniając poziom rzeki chcemy aby w razie przekroczenia wartości krytycznej lub nawet niebezpiecznego zbliżenia się do tej wartości rzeka wywoływała specjalne metody które jej dostarczymy. Metody będą w stanie odpowiednio obsłużyć daną sytuację wyjątkową.

Na początku zdefiniujmy nasze delegacje. Jedna  public delegate void AboutToFlood(string msg);  będzie definiowała metodę, która ma być wywołana gdy wystąpi zagrożenie (przekroczenie 90% wartości maksymalnej). Druga która będzie wywoływana gdy wystąpi powódź, czyli przekroczenie wartości maksymalnej (  public delegate void Flooded(string msg);  ).

   1 public class River
   2     {
   3         (...)
   4 
   5         public delegate void AboutToFlood(string msg);
   6         public delegate void Flooded(string msg);
   7 
   8         private AboutToFlood aboutToFloodListener;
   9         private Flooded floodedListener;
  10 
  11         (...)
  12     }

Dwie pierwsze deklaracje delegacji zaczynające się od  public delegate  stanowią same deklaracje, czyli specyfikację naszych delegacji. Dwa pozostałe, prywatne atrybuty  private AboutToFlood aboutToFloodListener;  oraz  private Flooded floodedListener;  będą przechowywały wskaźniki do odpowiednich metod.

Rozwińmy kod i dodajmy do niego dwie metody, które będą rejestrowały metody do naszych delegacji. ' Rejestrowały ' czyli ustawią atrybuty prywatne tak aby wskazywały na podane metody.

   1 public class River
   2     {
   3         (...)
   4 
   5         public delegate void AboutToFlood(string msg);
   6         public delegate void Flooded(string msg);
   7 
   8         private AboutToFlood aboutToFloodListener;
   9         private Flooded floodedListener;
  10 
  11         public void OnAboutToFlood(AboutToFlood aboutToFlood)
  12         {
  13             aboutToFloodListener = aboutToFlood;
  14         }
  15 
  16         public void OnFlooded(Flooded flooded)
  17         {
  18             floodedListener = flooded;
  19         }
  20 
  21         (...)
  22     }

Dodajmy jeszcze wywołanie metod zapamiętanych w naszych delegacjach w momencie wystąpienia sytuacji wyjątkowych, czyli zbliżenia i przekroczenia wartości maksymalnej.

   1     public class River
   2     {
   3         (...)
   4 
   5         public int Level
   6         {
   7             set
   8             {
   9                 riverLevel = value;
  10                 if (riverLevel > riverLevelMax)
  11                 {
  12                     // na poczatku sprawdzamy, czy nasza delegacja jet zainicjalizowana
  13                     if (floodedListener != null)
  14                         // jeżeli tak, to wywolujemy metode, podajac nazwe delegacji i przekazujac argument
  15                         floodedListener("The river " + name + " has flooded... ");
  16                 }
  17                 else
  18                 {
  19                     if (riverLevel > (riverLevelMax - (riverLevelMax / 10)))
  20                         // podobnie jak poprzednio sprawdzamy, czy delegacja jest zainicjalizowana
  21                         if (aboutToFloodListener != null)
  22                             // jezeli jest, to ja wywolujemy
  23                             aboutToFloodListener("The river " + name + " is about to flood... ");
  24                 }
  25             }
  26             get { return riverLevel; }
  27         }
  28 
  29        (...)
  30 
  31      } 

Pełny program w C#:

   1 using System;
   2 using System.Collections.Generic;
   3 using System.Text;
   4 
   5 namespace RiverLevelDelegates
   6 {
   7 
   8     public class River
   9     {
  10         private string name;
  11         private int riverLevel = 0;
  12         private int riverLevelMax = 10;
  13 
  14         public delegate void AboutToFlood(string msg);
  15         public delegate void Flooded(string msg);
  16 
  17         private AboutToFlood aboutToFloodListener;
  18         private Flooded floodedListener;
  19 
  20         public void OnAboutToFlood(AboutToFlood aboutToFlood)
  21         {
  22             aboutToFloodListener = aboutToFlood;
  23         }
  24 
  25         public void OnFlooded(Flooded flooded)
  26         {
  27             floodedListener = flooded;
  28         }
  29 
  30         public River(string riverName)
  31         {
  32             name = riverName;
  33         }
  34 
  35         public override string ToString()
  36         {
  37             return name;
  38         }
  39 
  40         public int Level
  41         {
  42             set
  43             {
  44                 riverLevel = value;
  45                 if (riverLevel > riverLevelMax)
  46                 {
  47                     if (floodedListener != null)
  48                         floodedListener("The river " + name + " has flooded... ");
  49                 }
  50                 else
  51                 {
  52                     if (riverLevel > (riverLevelMax - (riverLevelMax / 10)))
  53                         if (aboutToFloodListener != null)
  54                             aboutToFloodListener("The river " + name + " is about to flood... ");
  55                 }
  56             }
  57             get { return riverLevel; }
  58         }
  59     }
  60 
  61     class Program
  62     {
  63         static void AboutToFlood_Handler(string msg)
  64         {
  65             Console.WriteLine("Warning! {0}", msg);
  66         }
  67 
  68         static void Flooded_Handler(string msg)
  69         {
  70             Console.WriteLine("Critical! {0}", msg);
  71         }
  72 
  73         static void Main(string[] args)
  74         {
  75             River river = new River("Wisla");
  76 
  77             Console.WriteLine("Now we exceed the maximum level, but no message is expected, because we have not defined the delegates objects.");
  78             for (int i = 1; i < 15; i++) 
  79             {
  80                 Console.WriteLine("Setting river level to {0}", i);
  81                 river.Level = i;
  82             }
  83 
  84             // w naszym programie musimy jeszcze zainicjalizowac odpowiednie delegacje,
  85             // tworzac dla kazdej metody nowy obiekt delegacji
  86             river.OnAboutToFlood( new River.AboutToFlood( AboutToFlood_Handler ) );
  87             river.OnFlooded(new River.Flooded(Flooded_Handler));
  88 
  89             Console.WriteLine("Now we expect the warning messages.");
  90             for (int i = 1; i < 15; i++)
  91             {
  92                 Console.WriteLine("Setting river level to {0}", i);
  93                 river.Level = i;
  94             }
  95 
  96             Console.ReadLine();
  97         }
  98     }
  99 }


2015-09-23 06:32