Delegaty to referencyjny typ danych, który przechowuje referencje do metody. Referencja ta może być zmieniana w trakcie wykonywania programu.

Delegaty są przede wszystkim wykorzystywane do implementacji zdarzeń oraz wywołań zwrotnych metod(call-back). Czym są wywołania zwrotne? Programista rejestruje metodę do późniejszego wywołania, natomiast biblioteka wywołuje ją w stosownym dla siebie czasie. Wszystkie delegaty niejawnie dziedziczą z klasy System.Delegate.


Deklarowanie delegatów

Deklaracja delegatu określa metody, które mogą być wywołane za jego pomocą. Delegat może się odnosić do metody, która ma taką samą sygnaturę jak delegat.

Definicja:
public delegate int MyDelegate (string s);

Powyższy delegat może być wykorzystany do odniesienia się do jakiejkolwiek metody, która ma jeden parametr typu string oraz zwraca zmienną całkowitą typu int.

Składnia:
delegate typ_zwracany nazwa_delegatu (lista_parametrow);

Instancja delegatu

Jeżeli delegat został już zadeklarowany, należy utworzyć obiekt delegatu korzystając ze słowa kluczowego new oraz powiązać go z konkretną metodą. Podczas tworzenia delegata, argument przekazywany do tego wyrażenia jest podobny do wywołania metody, z tym, że nie podaje się argumentów metody.

Przykład:
public delegate void PrintMessage(string s);
…
PrintMessage1=new PrintMessage(WriteToScreen);
PrintMessage2=new PrintMessage(WriteToFile);

Poniższy przykład pokazuje deklaracje, utworzenie instancji oraz użycie delegata, który może mieć referencje do metody przyjmującej liczbę całkowitą oraz zwracającej liczbę całkowitą:

using System;
namespace Delegaty
{
    delegate int ChangeNumber(int i);
    class Program
    {
        static int number = 5;
        public static int AddNumber(int i)
        {
            number += i;
            return number;
        }
        public static int MultiplyNumber(int i)
        {
            number *= i;
            return number;
        }
        public static int GetNumber()
        {
            return number;
        }
        static void Main(string[] args)
        {
            // tworzenie instancji delegatów
            ChangeNumber cn1 = new ChangeNumber(AddNumber);
            ChangeNumber cn2 = new ChangeNumber(MultiplyNumber);
            // wywoływanie metod używajac delegatów
            cn1(5);
            Console.WriteLine("Wartość liczby: {0}", GetNumber());
            cn2(10);
            Console.WriteLine("Wartość liczby: {0}", GetNumber());
            Console.ReadKey();
            // Wynik działania programu
            // Wartosc liczby: 10
            // Wartosc liczby: 100
        }
    }
}

Delegaty złożone

Obiekty delegata mogą być łączone za pomocą operatora (+). Delegat złożony wywołuje dwa delagaty tak jakby były złączone. Jedynie delegaty tego samego typu mogą być ze sobą złożone. Operator (-) może być użyty do usunięcia danego delegatu z delagata złożonego.

Korzystając z tej właściwości delegatów można utworzyć listę delegatów, które zostaną wywołane, w momencie wywołania delegatu złożonego. Operacja taka to tzw. multicasting. Poniższy program pokazuje przykład wykorzystania takiej operacji:

Przykład:
using System;
namespace Delegaty
{
    delegate int ChangeNumber(int i);
    class Program
    {
        static int number = 5;
        public static int AddNumber(int i)
        {
            number += i;
            return number;
        }
        public static int MultiplyNumber(int i)
        {
            number *= i;
            return number;
        }
        public static int GetNumber()
        {
            return number;
        }
        static void Main(string[] args)
        {
            // tworznie instancji delegata złożonego
            ChangeNumber cn;
            // tworzenie instancji delegatów
            ChangeNumber cn1 = new ChangeNumber(AddNumber);
            ChangeNumber cn2 = new ChangeNumber(MultiplyNumber);
            cn = cn1;
            cn += cn2;
            // Wywołanie delegata złożonego
            cn(5);
            Console.WriteLine("Wartość liczby: {0}", GetNumber());
            Console.ReadKey();
            // Wynik działania programu
            // Wartosc liczby: 50
        }
    }
}

Przykład użycia delagatów

Poniższy przykład pokazuje zastosowania delegatów. Delegat PrintMessage może być wykorzystany jako referencja do metod, które przyjmują parametr typu string oraz nie zwracając niczego. Typ metody: void.

Delegat zostanie użyty do wywołania dwóch metod, pierwsza z nich wypisuje informacje w konsoli, druga w pliku tekstowym:

using System;
using System.IO;
namespace DelegatyPrzyklad
{
    class Program
    {
        static FileStream fs;
        static StreamWriter sw;
        // deklaracja delegatu
        public delegate void PrintMessage(string s);
        // Metoda wypisująca wiadomość w konsoli
        public static void WriteToConsole(string s)
        {
            Console.WriteLine("Wiadomość: {0}", s);
        }
        public static void WriteToFile(string s)
        {
            // FileMode.Append - jeżeli uruchomicie program kilka razy za każdym razem do 
            // pliku tekstowego zostanie dodana kolejna linika tekstu
            fs = new FileStream("c:\\wiadomosc.txt", FileMode.Append, FileAccess.Write);
            sw = new StreamWriter(fs);
            // Zapisujemy dane do pliku
            sw.WriteLine(s);
            // Czyszczenie naszego bufora
            sw.Flush();
            // Zamykamy obydwa strumienie
            sw.Close();
            fs.Close();
        }
        // Metoda jako parametr przyjmuje delagat
        // Oraz wywołuje dany delegat ze stosowną wiadomością
        public static void SendString(PrintMessage pm)
        {
            pm("Witaj Świecie");
        }
        static void Main(string[] args)
        {
            PrintMessage pm1 = new PrintMessage(WriteToConsole);
            PrintMessage pm2 = new PrintMessage(WriteToFile);
            SendString(pm1);
            SendString(pm2);
            Console.ReadKey();
            // Wynik działania programu
            // Wiadomosc: Witaj Swiecie
            // Oraz proszę spojrzeć na dysk C, został utworzony nowy plik
            // Wewnątrz tekst: Witaj Świecie
        }
    }
}