Delegate (CLI)

Ein Delegat ist eine Form des Typsicherheitsfunktionszeigers, der von der Common Language Infrastructure (CLI) verwendet wird. Delegates legen eine Methode zum Aufrufen und optional ein Objekt für den Methodenaufruf fest. Delegates werden unter anderem eingesetzt, um Callbacks und Event-Listener zu implementieren. Ein Delegatobjekt kapselt einen Verweis auf eine Methode. Das Delegatobjekt kann dann an ein Stück Code übergeben werden, der die referenzierte Methode aufrufen kann, ohne zur Kompilierzeit wissen zu müssen, welche Methode aufgerufen wird.

Ein Multicast-Delegat ist ein Delegat, das auf mehrere Methoden verweist.[1][2] Die Multicast-Delegierung ist ein Mechanismus, der Funktionalität bereitstellt, um mehr als eine Methode auszuführen. Es gibt eine Liste von Delegates, die intern gepflegt werden. Wird nun der Multicast-Delegat aufgerufen, wird diese Liste der Delegates ausgeführt.

In C# werden häufig Delegates verwendet, um Rückrufe in der ereignisgesteuerten Programmierung zu implementieren. Beispielsweise kann ein Delegat verwendet werden, um anzugeben, welche Methode aufgerufen werden soll, wenn der Benutzer auf eine Schaltfläche klickt. Delegates erlauben dem Programmierer, mehrere Methoden vom Eintreten eine Ereignisses zu benachrichtigen.[3]

C#-Codebeispiel

Der Code unten deklariert ein delegate-Typ, heißt SendMessageDelegate, nimmt als Parameter eine Message und gibt ein void zurück:

delegate void SendMessageDelegate(Message message);

Code, um eine Methode zu definieren, die ein instantiiertes Delegat als Argument annimmt:

void SendMessage(SendMessageDelegate sendMessageDelegateReference)
{
  // Rufe das Delegate und jedes andere verbundene Delegate synchron auf
  sendMessageDelegateReference(new Message("hello this is a sample message"));
}

Die implementierte Methode, die ausgeführt wird, wenn das Delegat aufgerufen wird:

void HandleSendMessage(Message message)
{
  // Die Implementation für die Sender- und die Message-Klasse sind in diesem Beispiel irrelevant
  Sender.Send(message);
}

Code, um die SendMessageDelegate-Methode aufzurufen, wobei ein instantiiertes Delegat als Argument übergeben wird:

SendMessage(new SendMessageDelegate(HandleSendMessage));

Delegates (C#)

delegate void Notifier(string sender);  // Normale Signatur mit dem Schlüsselwort delegate

Notifier greetMe;                       // Delegate-Variable

void HowAreYou(string sender) {
  Console.WriteLine("How are you, " + sender + '?');
}

greetMe = new Notifier(HowAreYou);

Eine Delegatvariable ruft die zugehörige Methode auf und wird selber wie folgt aufgerufen:

greetMe("Anton");                       // Ruft HowAreYou("Anton") auf und druckt "How are you, Anton?" aus

Delegatvariablen sind First-Class-Objekts der Form new DelegateType(obj.Method) und können jeder passenden Methode oder dem Wert null zugeordnet werden. Sie speichern eine Methode und ihren Empfänger ohne Parameter:[4]

new DelegateType(funnyObj.HowAreYou);

Das Objekt funnyObj kann this sein und weggelassen werden. Wenn die Methode static ist, sollte sie nicht das Objekt sein (auch als Instanz in anderen Sprachen bezeichnet), sondern die Klasse selbst. Es sollte nicht abstract sein, sondern könnte new, override oder virtuel.

Um eine Methode erfolgreich mit einem Delegat aufzurufen, muss die Methodensignatur dem DelegateType mit derselben Anzahl gleichartiger Parameter (ref, out, value) und demselben Typ (einschließlich Rückgabetyp) entsprechen.

Multicast-Delegates (C#)

Eine Delegat-Variable kann mehrere Werte gleichzeitig enthalten:

void HowAreYou(string sender) {
  Console.WriteLine("How are you, " + sender + '?');
}

void HowAreYouToday(string sender) {
  Console.WriteLine("How are you today, " + sender + '?');
}

Notifier greetMe;

greetMe = HowAreYou;
greetMe += HowAreYouToday;

greetMe("Leonardo");                  // "How are you, Leonardo?"
                                      // "How are you today, Leonardo?"
greetMe -= HowAreYou;

greetMe("Pereira");                   // "How are you today, Pereira?"

Wenn der Multicast-Delegat eine Funktion ist oder kein Parameter out hat, wird der Parameter des letzten Aufrufs zurückgegeben.[5]

Technische Details

Obwohl interne Implementierungen variieren können, können Delegate-Instanzen als Tupel eines Objekts und eines Methodenzeigers und eines Verweises (möglicherweise null) auf einen anderen Delegaten gedacht werden. Daher ist ein Verweis auf einen Delegierten möglicherweise ein Hinweis auf mehrere Delegierte. Wenn der erste Delegat beendet ist, wenn seine Kettenreferenz nicht null ist, wird der nächste aufgerufen, und so weiter, bis die Liste vollständig ist. Dieses Muster ermöglicht es einem Ereignis, Overhead-Skalierung leicht von dem eines einzelnen Verweises bis zum Versand zu einer Liste von Delegaten zu haben, und ist weit verbreitet in der CLI verwendet.

Leistung

Die Leistung von Delegates war viel langsamer als eine virtuelle oder Interface-Methode aufzurufen (ein 1/6 bis 1/8 Mal so schnell in Microsoft 2003 Benchmarks),[6] aber seit dem .NET 2.0 CLR im Jahr 2005 ist es ungefähr gleich schnell wie Interface-Aufrufe.[7] D.h., dass es einen kleinen zusätzlichen Overhead im Vergleich zu direkten Methodenaufrufen gibt.

Es gibt sehr strenge Regeln für den Bau von delegierten Klassen.

Siehe auch

Einzelnachweise

  1. How to: Combine Delegates (Multicast Delegates)(C# Programming Guide). msdn.microsoft.com, abgerufen am 20. Mai 2008.
  2. About Microsoft’s “Delegates”. In: Sun Developer Network. Sun Microsystems, archiviert vom Original am 10. Februar 1999;.
  3. Wikibooks: C Sharp Programming/Delegates and Events.
  4. Hanspeter Mössenböck: Advanced C#: Variable Number of Parameters. Institut für Systemsoftware, Johannes Kepler Universität Linz, Fachbereich Informatik, 25. März 2002, S. 23–24, abgerufen am 4. August 2011.
  5. Hanspeter Mössenböck: Advanced C#: Variable Number of Parameters. Institut für Systemsoftware, Johannes Kepler Universität Linz, Fachbereich Informatik, 25. März 2002, abgerufen am 4. August 2011.
  6. Jan Gray: Writing Faster Managed Code: Know What Things Cost. Microsoft, Juni 2003, abgerufen am 9. September 2007.
  7. Oliver Sturm: Delegate calls vastly sped up in .NET 2. 1. September 2005, abgerufen am 9. September 2007.