System.Delegate und System.EventHandler
Die
FCL bietet Delegate, diese sind verwaltete Zeiger (managed pointer) welche eingesetzt werden können um Ereignishandler und Callback-Funktionen im .NET Framework zu nutzen. Die Standardimplementierung von
System.Delegate ermöglicht es einem Delegat hinzuzufügen bzw. zu entfernen, sowie die darin enthaltenen Methoden.
System.Delegate ist die Basisklasse für alle Delegate.
System.MulticaseDelegate ist von
System.Delegate abgeleitet und bietet die Möglichkeit mehrere Methode aufzurufen. Wenn ein Delegat nicht von
System.MulticastDelegate abgeleitet ist, so kann es nur max. eine Referenz auf eine Methode enthalten.
Eigene Delegate erstellen
Jeder .NET compiler sollte die Unterstützung von Delegaten über Schlüsselwörter oder eine spezielle Syntax unterstützen. C# bietet das Schlüsselwort
delegate:
Code:
[b]public delegate void [/b]SampleEventHandler([b]object [/b]sender, EventArgs args);
Beachte, dass der Delegate-Name auf
EventHandler endet. Das ist ein Standard und Microsoft empfiehlt alle Delegate so zu benennen, es ist jedoch keine Pflicht.
Weiterhin gilt es zu beachten, dass die Signatur des Delegats keinen Rückgabewert hat, der erste Parameter vom Typ
object ist und der zweite Parameter vom Typ
EventArgs. Diese Signatur trifft man sehr häufig in der
FCL an und wird auch von Microsoft empfohlen, wiederum ist auch dieses keine Verpflichtung. Wie auch immer, diese Signatur wird so häufig genutzt, dass sie ihr eigenes Delegat besitzt:
System.EventHandler. Durch Festlegung ist der erste Parameter immer die Instanz, welche die Methode aufgerufen hat. Der zweite Parameter enthält die Argumente, welche das Ereignis beschreiben.
Wenn man mehr Informationen an ein Delegat weiterleiten möchte, kann man eine eigene Klasse von
EventArgs ableiten und die benötigten Eigenschaften darin definieren:
Code:
[b]public class [/b]MyEventArgs : EventArgs
{
[b]public [/b]MyEventArgs(string mySpecialValue)
{
MySpecialValue = mySpecialValue;
}
[b]public string[/b] MySpecialValue;
}
[b]public delegate void[/b] SampleSpecialEventHandler([b]object [/b]sender, MyEventArgs args);
Wenn das Delegat aufgerufen wird, dann kann die aufgerufene Methode auf
args.MySpecialValue zugreifen.
Einen eigenen Delegat in Delphi zu erstellen ist recht einfach und bekannt, einfach die bekannte Methodentypendeklaration nutzen:
Delphi-Quellcode:
type
TSampleEventHandler = procedure(Sender: TObject; Args: EventArgs);
Erstellen und Nutzen eines Delegates
In C# erstellt man ein Multicast-Delegat mit Hilfe des Schlüsselwortes
event:
Code:
[b]public event [/b]SampleEventHandler EventTest;
Will man einem Ereignis einen (weiteren) Ereignishandler zufügen, so muss man in C# die
+= Syntax nutzen:
Code:
[b]private void [/b]WinForm_Load([b]object [/b]sender, System.EventArgs e)
{
EventTest += [b]new [/b]SampleEventHandler(OnMyEvent);
}
[b]private void [/b]OnMyEvent([b]object [/b]sender, EventArgs args)
{
MessageBox.Show("OnMyEvent called!");
}
Der C# Compiler erkennt die Zuweisung und ersetzt die
+= Syntax intern durch einen Aufruf zu
System.Delegate.Combine, um
SampleEventHandler der Liste in
EventTest hinzuzufügen. Es ist in C# möglich dieses Konstrukt auch zu erweitern und wie folgend zu schreiben:
Code:
// Example of how to "roll out" an event handler
[b]private [/b]SampleEventHandler otherEvent;
[b]private event [/b]SampleEventHandler AnotherEventTest
{
[b]add[/b]
{
otherEvent =
(SampleEventHandler)Delegate.Combine(otherEvent, value);
}
[b]remove[/b]
{
otherEvent =
(SampleEventHandler)Delegate.Combine(otherEvent, value);
}
}
Beachte die Nutzung von
Delegate.Combine und die implizite Übergabe des
Wertetypen als Parameter.
Die allgemeine Art einen einfachen Ereignisshandler in Delphi zu schreiben sieht wie folgend aus:
Delphi-Quellcode:
type
TSampleEventHandler = procedure(Sender: TObject; Args: EventArgs);
TWinForm18 = class(System.Windows.Forms.Form)
private
FSingleSampleEventHandler: TSampleEventHandler;
property SampleEventHandler: TSampleEventHandler
read FSingleSampleEventHandler write FSingleSampleEventHandler;
end;
Um mehrfache (multi-cast) Ereignishandler zu schreiben muss man die Schlüsselwörter
add und
remove nutzen:
Delphi-Quellcode:
type
TSampleEventHandler = procedure(Sender: TObject; Args: EventArgs);
TWinForm18 = class(System.Windows.Forms.Form)
private
FMultiSampleEventHandler: TSampleEventHandler;
property MultiSampleEventHandler: TSampleEventHandler
add FMultiSampleEventHandler remove FMultiSampleEventHandler;
end;
Anstelle der (neuen) Standardsyntax für Mehrfach-Ereignishandler (hier
FMultiSampleEventHandler) kann man die Implementierung auch manuell gestallten:
Delphi-Quellcode:
FMultiSampleEventHandlerByHand: TSampleEventHandler;
{ Multi-cast event handler roll out methods }
procedure AddMultiEvent(Value: TSampleEventHandler);
procedure RemoveMultiEvent(Value: TSampleEventHandler);
{ Multi-cast event, rolled out by hand }
property MultiSampleEventHandlerByHand: TSampleEventHandler
add AddMultiEvent remove RemoveMultiEvent;
// Die Implementierung gestalltet sich etwas umständlich, wenn man die Syntax dafür nicht kennt:
procedure TWinForm18.AddMultiEvent(Value: TSampleEventHandler);
begin
FMultiSampleEventHandlerByHand := TSampleEventHandler(
Delegate.Combine(@FMultiSampleEventHandlerByHand, @Value));
end;
procedure TWinForm18.RemoveMultiEvent(Value: TSampleEventHandler);
begin
FMultiSampleEventHandlerByHand := TSampleEventHandler(
Delegate.Remove(@FMultiSampleEventHandlerByHand, @Value));
end;
Man beachte den Einsatz von
@, um die Adresse des Ereignisses zu ermitteln; wird @ nicht genutzt, wird der Ereignishandler direkt aufgerufen. Man beachte auch den Cast des Ereignistypen, genauso wie auch in C#.
In Delphi kann man weiterhin die Standard Art nutzen, um einen Ereignishandler zu setzen:
Delphi-Quellcode:
procedure TWinForm18.TWinForm18_Load(sender: System.Object;
e: System.EventArgs);
begin
SampleEventHandler := OnSampleEvent;
end;
Will man jedoch auf mulit-casts nicht verzichten, so muss man
Include(...) nutzen:
Delphi-Quellcode:
procedure TWinForm18.TWinForm18_Load(sender: System.Object;
e: System.EventArgs);
begin
Include(MultiSampleEventHandler, OnSampleEvent);
end;
Um einen Ereignishandler zu entfernen muss man
Exclude(...) nutzen.
Der Aufruf der Ereignishandler gestaltet sich in Delphi ähnlich wie in C#. Zuerst muss man überprüfen, ob der Handler
nil ist, anschließend ruft man diesen wie eine Methode auf:
Code:
[i]// C# Syntax[/i]
[b]if [/b](EventTest != [b]null[/b])
EventTest(this, [b]new [/b]EventArgs());
Delphi-Quellcode:
// Delphi Syntax
if Assigned(FSingleSampleEventHandler) then
FSingleSampleEventHandler(Self, EventArgs.Create);
Notiz meinerseits: "delegate" (englisch) wird im Deutschen i.A. mit "Delegate(n)" übersetzt und ist nichts weiter als ein schönes Wort für "statische Callback Funktionen"