Einzelnen Beitrag anzeigen

Benutzerbild von sakura
sakura

Registriert seit: 10. Jun 2002
Ort: München
11.412 Beiträge
 
Delphi 11 Alexandria
 
#4

Re: Einführung in die .NET Framework Klassen Bibliothek

  Alt 11. Okt 2004, 13:50
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"
Daniel W.