Hallo mal wieder (zu später Stunde).
Delphi treibt mich in den Wahnsinn. Warum kann ein Konzept nicht durchgängig angewendet werden? *seufz*
Also, ich arbeite gerade an der Implementation eines Observer-Patterns:
http://www.codeproject.com/KB/archit...erPattern.aspx
Meine Idee ist es, wenn ein Ereignis eintritt, mehrere andere Komponenten zu benachrichtigen (teilweise verschiedene Vorfahren). Siehe auch Ursprungs-Thread:
http://www.delphipraxis.net/internal...t.php?t=162559
Einfaches (sinnloses) Beispiel:
Beim Programmstart werden die TNotifyEvent am Observer angemeldet. Beim Klick auf Button1 erscheint zuerst '111', direkt danach '222'.
Delphi-Quellcode:
procedure TForm1.Button6Click(Sender: TObject);
begin
ShowMessage('111'); // Problem: an dieser Stelle hat die Variable "Self" den Wert NIL
end;
procedure TForm1.Button7Click(Sender: TObject);
begin
ShowMessage('222'); // Problem: an dieser Stelle hat die Variable "Self" den Wert NIL
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Observer := TMeinSelbstProgrammierterObserver.Create(Self);
Observer.RegisterSubscriber(Button6Click);
Observer.RegisterSubscriber(Button7Click);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Observer.NotifySubscriber;
end;
Das ganze funktioniert auch. In diesem Beispiel.
Das Problem ist, dass beim Aufruf z.B. von Button6Click (über Observer.NotifySubscriber) "Self" = NIL ist.
Delphi-Quellcode:
procedure THAL_Observer.NotifySubscriber(_Sender: TObject);
var
i: integer;
FNotifyEvent: TNotifyEvent;
begin
try
FNotifyEvent :=
NIL;
for i := 0
to FListSubscriber.Count - 1
do begin
if assigned(FListSubscriber.Items[i])
then begin
@FNotifyEvent := FListSubscriber.Items[i];
FNotifyEvent(Self);
end;
end;
except
on E:
Exception do begin
end;
end;
end;
Obwohl der Callback (sagt man so?) tatsächlich aus dem Haupt-Thread heraus erfolgt, wird scheinbar diese Prozedur ohne Bezug zu der Konkreten Instanz des Objektes gespeichert. Na toll.
Wenn ich einem TButton.OnClick ein TNotifyEvent zuweise, ist "Self" beim aufrufen doch zugewiesen. Warum also nicht wenn ich das TNotifyEvent dirket aufrufe, wenn ich den Pointer dazu in einer TList habe?
Am fehlenden/falschen Sender kann es ja nicht liegen; der darf ja keinen Einfluss auf "Self" haben.
Wie bekomme ich das nun am einfachsten hin? Gibt es einen Trick, um den Pointer auf die Prozedur der konkreten Instanz des Objektes in der Liste abzuspeichern, oder was kann ich tun?
Ich will nicht für jedes Objekt, das benachrichtigt werden will, ein Interface implementieren um "zurückgerufen" zu werden.
Ich meine, wofür gibt es TNotifyEvent, wenn ich noch nicht mal von
genau diesem Objekt genau diese Prozedur bei einem anderen Objekt hinterlegen kann?
Danke im Vorraus. Für verwendbare Alternativen wäre ich auch dankbar.
Delphi-Quellcode:
unit HAL1_CLASSES_Observer;
// ***************************************************************************//
// THAL_Observer
// RegisterSubscriber: Parameter nennt die Prozedur, die beim Ereinis aufgerufen wird
// UnRegisterSubscriber: Komponente wird gelöscht (o.ä.), deshalb keine weiteren Infos mehr
//
// ***************************************************************************//
interface
{$REGION 'uses'}
uses
Classes, Contnrs, Controls, Dialogs, ExtCtrls, Forms, Graphics, Messages,
StdCtrls, SysUtils, Variants, Windows
;
{$ENDREGION}
type
THAL_Observer =
class(TComponent)
private
FListSubscriber: TList;
public
procedure NotifySubscriber(_Sender: TObject);
procedure RegisterSubscriber(_Event: TNotifyEvent);
procedure UnRegisterSubscriber(_Event: TNotifyEvent);
constructor Create(_Sender: TComponent);
override;
destructor Destroy;
override;
end;
implementation
{==============================================================================}
{$Region 'constructor THAL_Observer.Create(_Sender: TComponent);'}
constructor THAL_Observer.Create(_Sender: TComponent);
begin
try
inherited Create(_Sender);
FListSubscriber := TList.Create;
except
on E:
Exception do begin
end;
end;
end;
{$EndRegion}
{==============================================================================}
{$Region 'destructor THAL_Observer.Destroy;'}
destructor THAL_Observer.Destroy;
begin
try
FreeAndNil(FListSubscriber);
inherited Destroy;
except
on E:
Exception do begin
end;
end;
end;
{$EndRegion}
{==============================================================================}
{$Region 'procedure THAL_Observer.NotifySubscriber(_Sender: TObject);'}
procedure THAL_Observer.NotifySubscriber(_Sender: TObject);
var
i: integer;
FNotifyEvent: TNotifyEvent;
begin
try
FNotifyEvent :=
NIL;
for i := 0
to FListSubscriber.Count - 1
do begin
if assigned(FListSubscriber.Items[i])
then begin
@FNotifyEvent := FListSubscriber.Items[i];
FNotifyEvent(Self);
end;
end;
except
on E:
Exception do begin
end;
end;
end;
{$EndRegion}
{==============================================================================}
{$Region 'procedure THAL_Observer.RegisterSubscriber(_Event: TNotifyEvent);'}
procedure THAL_Observer.RegisterSubscriber(_Event: TNotifyEvent);
begin
try
if FListSubscriber.IndexOf(@_Event) < 0
then begin
FListSubscriber.Add(@_Event)
end;
except
on E:
Exception do begin
end;
end;
end;
{$EndRegion}
{==============================================================================}
{$Region 'procedure THAL_Observer.UnRegisterSubscriber(_Event: TNotifyEvent);'}
procedure THAL_Observer.UnRegisterSubscriber(_Event: TNotifyEvent);
begin
try
if FListSubscriber.IndexOf(@_Event) > -1
then begin
FListSubscriber.Extract(@_Event);
end;
except
on E:
Exception do begin
end;
end;
end;
{$EndRegion}
{==============================================================================}
end.