Verfasst von: bletra | 16. September 2010

Delegates (Teil 1 von 6): Einführung

Für C++ Programmierer sind Delegate typsichere Funktionszeiger. Für andere einfach ein Datentyp, der Methoden aufnehmen kann. Dies ist für Python- Entwicklerinnen oder Anhängern der funktionale Programmierung keine Besonderheit. Für .Net sind Delegates die Basis des Eventhandlings und für asynchrone Programme. In dieser Artikelserie möchte ich auf die Rolle von Delegates in .Net eingehen.

  • Teil 1: Einführung (dieser Artikel)
  • Teil 2: Generische Delegate
  • Teil 3: Multicast Delegate
  • Teil 4: Events
  • Teil 5: Asynchrone Delegate
  • Teil 6: Lamba-Ausdrücke

Wie wird ein Delegat definiert? Betrachten wir folgendes einfaches Beispiel:

delegate double MathDelegate(double angle);
MathDelegate calculator;
calculator = Math.Sin //oder auch = new MathDelegate(Math.Sin)
System.Diagnostics.Debug.WriteLine(calculator(45))

Zeile 1 definiert einen bestimmten Delegattyp (MathDelegate), Zeile 2 deklariert eine Delegatvariable (calculator) und Zeile 3 instanziiert sie mit einer statischen Methode. Die Delegatvariable wird nun wie die Methode die sie enthält verwendet (Zeile 4).

Was passiert dabei im Hintergrund? In .Net sind alles Objekte, von welcher Klasse ist also ‚calculator‘ eine Instanz? Durch das Schlüsselwort delegate in Zeile 1 wird eine neue Klasse erzeugt, die von der Basisklasse System.MulticastDelegate abgeleitet wird. Zeile 1 definiert dabei auch genau, welche Art von Funktionen „aufgenommen“ werden können, nämlich nur die Methoden, die der angegebenen Signatur folgen, also ein double als Argument erwarten und ein double zurückgeben. Auf Multicast-Delegates, die wir also eigentlich immer erhalten, werden wir in Teil 3 dieser Serie genauer eingehen. Wichtig ist hierbei: Da wir von dieser Klasse erben, erben wir auch alle Methoden, Eigenschaften und Operatoren. Es lohnt sich also, die Referenz zu konsultieren: MulticastDelegate Class.

Im obigen Beispiel wurde eine statische Methode, also eine zustandslose Methode, verwendet. Delegates können aber auch Objektmethoden aufnehmen:

public class Discounter
{
  private string name;
  public Discounter(string name)
  {
    this.name = name;
  }
  public string GetGreeting()
  {
    return "Hi " + this.name;
  }
}

public class Shop
{
  delegate string GreetingDelegate();
  public void Test()
  {
    Discounter disc = new Discounter("donald");
    greet = disc.GetGreeting
    System.Diagnostics.Debug.WriteLine(greet())
  }
  …
}

Generell ist  zu beachten, dass private Methoden auch für Delegate privat bleiben, d.h. auch ein Delegat kann nicht auf eine private Methode einer anderen Klasse zugreifen. Abstrakte Methoden können nicht aufgenommen werden, es muss immer eine konkrete Implementation existieren. Virtual deklarierte Methoden, sind dagegen kein Problem.

Bezüglich der definierten Signatur eines Delegattypen ist wichtig, dass die aufzunehmenden Methoden nicht wirklich identische Signaturen aufweisen müssen, sondern abgeleitete Klassen bzgl. des Rückgabewertes und Basisklassen bzgl. der Argumente auch zulässig sind:

public delegate Employee HandlerMethod(Manager man);
HandlerMethod worker;
worker = FirstHandler;
public static Manager FirstHandler(Employee worker)
{
 …
}
public class Manager : Employee
{
…
}

Die Signatur der aufzunehmenden Methode darf abweichen:

  • Der Rückgabewert muss vom vorgegebenen oder einem davon abgeleiteten Objekttyp sein, d.h. das Delegat definiert Employee als geforderten Rückgabewert und akzeptiert alle Methoden, die Employee oder von Employee abgeleitete Klassen zurückgeben. Dies läuft unter dem Begriff Covariance.
  • Die Argumente müssen vom vorgegebenen oder einem dazugehörigen Basistyp sein, d.h. das Delegat definiert Manager als gefordertes Argument und akzeptiert alle Methoden, die Manager oder einen Basistyp von Manager als Argument erwarten. Dies läuft unter dem Begriff Contravariance.

Wird eine Funktion/Methode nur für ein Delegat benötigt, dann kann diese auch direkt als anonyme Funktion dem Delegat übergeben werden:

delegate void TestDelegate(string s);
TestDelegate testDelB = delegate(string s) { Console.WriteLine(s); };

Manche sprechen hierbei auch von anonymen Delegaten, wobei ich dies eher irreführend finde. Neben der hier dargestellten Schreibweise gibt es eine weitere, noch kürzere Darstellung, die sich Lambda-Ausdruck nennt, auf die wir in Teil 6 dieser Artikelserie noch einmal eingehen werden:

delegate void TestDelegate(string s);
TestDelegate testDelC = (x) => { Console.WriteLine(x); };

Delegates werden sehr häufig für Sortierfunktionen verwendet, wie folgendes Beispiel von Stackoverflow.com zeigt:

public string SortByDateModified(List<CartItem> items)
{
 items.Sort(delegate(CartItem itemA, CarItem itemB)
 {
   return itemA.DateModified.CompareTo(itemB.DateModified);
 });
}

Wann können Delegate eingesetzt werden? Immer dann, wenn mehr als eine konkrete Implementation einer Methode relevant ist. Delegate sind somit auf Methodenebene mit Schnittstellen (Interfaces) auf Klassenebene vergleichbar. Sie können auch immer dann verwendet werden, wenn über ein Interface mit nur einer Methode nachgedacht wird. Sie können zur Umsetzung folgender Entwurfsmuster verwendet werden: Adapter, Command, Mediator, Chain, Observer, Visitor. Seit 2005 sind Delegate laut Microsoft genauso performant wie Schnittstellen (Interfaces). Delegate sind natürlich Referenztypen, die auf dem Heap liegen.

Advertisements

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

Kategorien

%d Bloggern gefällt das: