Delegates in C#

Insbesondere in der objektorientierten Welt von .NET hat man oftmals die Anforderung Code auszuführen, der an einer vollkommen anderen Stelle definiert ist oder dessen genauen Inhalt man nicht kennt. Ich wurde schon in vielen Kursen von meinen Teilnehmern gefragt, welche Möglichkeiten uns in diesem Fall zur Verfügung stehen. In solchen Fällen bietet sich oft die Verwendung von Delegaten an. Ein Delegat ist an sich nur ein Datentyp, der auf eine Methode zeigt. Der Aufrufer der Methode wird also zur Laufzeit mit der Zielmethode verbunden. Eine mitgegebene Liste an Parametern fungiert hierbei als Signatur für die Art von Methoden, auf die einen Delegaten verweisen darf.

Hier müssen wir zwischen den zwei Ausprägungen eines Delegaten unterscheiden: Typ und Instanz.

Ein Delegat-Typ definiert sozusagen die „Vorlage“, an das sich der Aufrufer und das Ziel halten müssen. Hier werden Rückgabetyp und die Parameter definiert.
Eine Delegat-Instanz ist ein Objekt, welches sich auf eine oder mehrere Zielmethoden bezieht. Dieses Objekt entspricht der vorher definiertem „Vorlage“.

Die Deklaration eines Delegates beginnt mit dem delegate Schlüsselwort, gefolgt vom Rückgabetyp, dem Namen des Delegat-Typen und der Parameterliste.

delegate void MyDelegate(int x);

Um eine Delegat-Instanz zu erstellen, muss man einer Delegat-Variable eine Methode zuweisen. Dies kann zum Beispiel folgendermaßen aussehen:

        static void Main(string[] args)

        {

            MyDelegate d = new MyDelegate(A);

            d(12);

        }

 

        static void A(int x)

        {

            // Hier passiert etwas ...

        }       

Nachdem man der Delegat-Variable eine Methode zugewiesen hat, kann man mit ihr die hinterlegte Methode aufrufen.

Wenn man innerhalb seiner Anwendung einen Delegaten benötigt, der auf eine Methode zeigt, die einen oder mehrere Parameter hat, jedoch keinen Rückgabetyp benötigt, kann man entweder seinen eigenen Delegaten schreiben oder den bereits vordefinierten generischen Action<T> - Delegaten aus dem .NET Framework nutzen. Mithilfe des Action<T> - Delegaten können wir Methoden ausführen, die bis zu 16 Parameter haben.

        static void Main(string[] args)

        {

            var act = new Action<int, int>(B);

            act(1,2);

        }

 

        static void B(int zahl1, int zahl2)

        {

            Console.WriteLine(zahl1 + zahl2);

        }

 

Im obrigen Codebeispiel wird ein Action<T> - Delegat mit zwei Integer Parameter erstellt, der auf die Methode B verweist. Beim Aufruf des Delegaten wird die Methode B ausgeführt und die Summe der übergebenen Parameter in die Konsole hineingeschrieben.

 

Für Methoden, die einen Wert zurückgeben, kann man den generischen Func<T> - Delegaten verwenden. Der Func<T> - Delegat kann, wie der Action<T> - Delegat, bis zu 16 Parameter nutzen. Der letzte generische Eintrag ist üblicherweise der Rückgabetyp.

 

        static void Main(string[] args)

        {

            var fun = new Func<int, int, string>(C);

            Console.WriteLine(fun(5, 3));

        }

 

        static string C(int zahl1,int zahl2)

        {

            return ("Das Ergebnis ist " + (zahl1 + zahl2)).ToString();

        }

 

In diesem Codebeispiel wird ein Func<T> - Delegat erstellt, der zwei Integer-Zahlen als Parameter verlangt und eine Rückgabe in Form eines Strings zurückgibt. Der String beinhaltet die Summe der übergebenen Zahlen mit einem Begleittext. Dieser Text wird nach dem Aufruf der Methode C in die Konsole hineingeschrieben.

 

Nun wird man sich sicherlich fragen, welche der Varianten man nun am besten nutzen sollte.

Prinzipiell könnte man, wenn man sich ein wenig Schreibarbeit sparen möchte und nicht mehr als 16 Parameter benötigt, die Action<T>und Func<T> Delegaten ohne Probleme verwenden. Jedoch sind die Modifizierer out und ref für die Parameter bei Action<T>und Func<T> nicht nutzbar. Für diesen Fall müsste man sich den Delegaten selbst schreiben.

 

Ein weiteres sehr nützliches Feature ist das Verketten von Delegaten. Mithilfe des += Operators kann man mit einem Delegaten nicht nur auf eine, sondern auf mehrere Zielmethoden verweisen.  Es ist hierbei sogar möglich, mehrere Delegate-Instanzen miteinander zu verketten. Dies wird im folgenden Beispiel kurz vorgestellt:

 

        delegate void MyDelegate(int x);

 

        static void Main(string[] args)

        {

            MyDelegate del1 = A;

            MyDelegate del2 = C;

 

            del1 += B;

            del1 += del2;

 

            del1(12);

        }

 

 

        static void A(int x)

        {

            Console.WriteLine(x);

        }

        static void B(int x)

        {

            Console.WriteLine(x * 2);

        }

        static void C(int x)

        {

            Console.WriteLine(x * 3);

        }

 

Hier werden 2 Delegaten erstellt, die je auf die Methode A und C zeigen. Die Methode B und C werden dann mithilfe des += Operators augenscheinlich an den Delegaten del1 angehängt. In Wirklichkeit wird im Hintergrund immer eine neue Delegat-Instanz erstellt, die der Variable del1 zugewiesen wird. Dies liegt an der Tatsache, dass Delegaten an sich unveränderliche Objekte sind. In C# werden daher für die Delegaten die += und -= Operationen in die statischen Methoden Combine() und Remove() der Klasse System.Delegate umgewandelt.

Mit dem Aufruf von del1 werden die Methoden A, B und C der Reihe nach ausgeführt. Die Reihenfolge hängt in erster Linie davon ab, wann eine Methode „angehängt“ wird.

Delegaten werden insbesondere im Zusammenhang mit Events sehr häufig verwendet. In einem künftigen Blogeintrag werde ich dann auf die genauen Zusammenhänge von Delegaten und Events eingehen.

 


Pingbacks and trackbacks (1)+

Kommentare sind geschlossen