Events in C#

Im Anschluss an meinen Blogartikel über Delegaten werden wir uns die Events nun ein wenig genauer anschauen. Prinzipiell ist die Datenkapselung ein sehr wichtiges Konzept in C#. Wir alle verwenden Properties um den Zugriff auf Felder bestmöglich einzuschränken. Die Problematik eines wahllosen Zugriffs liegt hierbei klar auf der Hand. Bei Delegaten gibt es an der Stelle ein ähnliches Problem:

    public class Button

    {

        public EventHandler clicked; 

        public void Click()

        {

            clicked(this, EventArgs.Empty);

        }

    }       


    static void Main(string[] args)

    {

        Button b = new Button();

 

        b.clicked += A;                             // Erlaubt

        b.Click();

 

        b.clicked(new object(), EventArgs.Empty);   // Verboten

        b.clicked = null;                           // Verboten

    }


In einem Konsolenprogramm existiert eine Klasse Button, die ich im Laufe dieses Artikels schrittweise erweitern werde. Dieser „Button“ hat eine Klick-Methode und einen Delegaten, der auf eine Methode außerhalb der Klasse (hier z.B A) verweist. Grundsätzlich können wir dem Delegaten mithilfe von += eine Methode anhängen. Mithilfe von Click() wird die Methode dann auch aufgerufen. So weit so gut.
Wir können aber auch noch ganz andere Sachen mit dem Delegaten machen. Wir könnten einfach so alle Methoden, die bei einem Delgaten hinterlegt sind, aufrufen. An der Stelle könnte man jetzt darüber streiten, ob das außerhalb einer Klasse zulässig sein sollte oder nicht. In unserem Fall gehen wir einmal davon aus, dass dieses Verhalten nicht wünschenswert ist, da die Methode Click() diese Aufgabe für uns übernehmen sollte. Insbesondere innerhalb von vielen Frameworks werden Delegaten nur innerhalb einer Klasse aufgerufen und nicht mitten in der Geschäftslogik.
Viel schlimmer als das simple Aufrufen ist aber das Setzen auf null. Alle Methoden in der Aufrufliste des Delegaten wären nun mit einem Schlag weg. Unser gesamtes „Benachrichtigungssystem“ ist somit nach nur einer einzigen Zeile Code nicht mehr vorhanden. Um solche schädlichen Zugriffe auf einen Delegaten zu verhindern, gibt es in .NET ein tolles Konzept: Events.

Delegaten werden in .NET mithilfe von Events gekapselt. Zwischen Delegaten und Events herrscht im übertragenen Sinne dieselbe Beziehung wie bei Feldern und Properties. Mithilfe eines Events können wir den Zugriff auf einen Delegaten steuern.

                private EventHandler clicked;


        public event EventHandler Clicked

        {

            add

            {

                clicked += value;

            }

            remove

            {

                clicked -= value;

            }

        }

In der Klasse Button habe ich nun ein sogenanntes explizites Event hinzugefügt. Wichtig hierbei ist das Schlüsselwort event und der Delegat-Typ, der in unserem Fall ein EventHandler ist. Anstelle von get und set haben wir bei Events etwas anders: add und  remove. Wie die Namen bereits andeuten, steuert add das Hinzufügen von Methoden und remove das Entfernen von Methoden. Nun ist es nicht mehr möglich dem Delegaten von außen aus null zuzuweisen oder ihn aufzurufen.

Das einzige unschöne an dieser Lösung ist die Tatsache, dass wir relativ viel Code benötigen um einen einzigen Delegaten zu kapseln. Ich will mir gar nicht vorstellen, wie das Ganze mit 20 oder gar 100 Events aussehen würde. Glücklicherweise gibt es ähnlich wie bei den Properties auch hier eine sehr einfache Lösung: die impliziten Events.

       public event EventHandler Clicked

public void Click()

        {

            Clicked(this, EventArgs.Empty);

        }


Hier lassen wir uns sehr viel Arbeit vom Compiler abnehmen. Da add und remove so gut wie immer den selben Aufbau haben, können wir uns das Generieren des Delegaten und das Ausfüllen von add und remove vom Compiler übernehmen lassen. Eine kleine Besonderheit gibt es hier aber dennoch: In der Methode Click() wird der Event wie ein Delegat verwendet, obwohl wir mit einem Event nur Methoden hinzufügen oder entfernen können. An der Stelle greift ebenfalls der Compiler ein und platziert hier den (korrekten) Aufruf des Delegaten.
Mithilfe dieser Schreibweise können wir uns also sehr viel Tipparbeit sparen und die gewonnene Arbeitszeit für die wichtigen Dinge im Leben eines Programmierers nutzen: Debuggen :)

Kommentare sind geschlossen