AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Object-Pascal / Delphi-Language Delphi TNotifyEvent Objektbezogen in Variable speichern
Thema durchsuchen
Ansicht
Themen-Optionen

TNotifyEvent Objektbezogen in Variable speichern

Ein Thema von berens · begonnen am 2. Sep 2009 · letzter Beitrag vom 23. Sep 2009
Antwort Antwort
berens

Registriert seit: 3. Sep 2004
431 Beiträge
 
Delphi 2010 Professional
 
#1

TNotifyEvent Objektbezogen in Variable speichern

  Alt 2. Sep 2009, 23:55
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.
  Mit Zitat antworten Zitat
Benutzerbild von sirius
sirius

Registriert seit: 3. Jan 2007
Ort: Dresden
3.443 Beiträge
 
Delphi 7 Enterprise
 
#2

Re: TNotifyEvent Objektbezogen in Variable speichern

  Alt 3. Sep 2009, 06:12
Du merkst dir hier einen Zeiger auf eine lokale Variable:
FListSubscriber.Add(@_Event) Wundersam, dass da überhaupt etwas funktioniert.
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.
  Mit Zitat antworten Zitat
KrasserChecker

Registriert seit: 21. Jul 2004
120 Beiträge
 
#3

Re: TNotifyEvent Objektbezogen in Variable speichern

  Alt 3. Sep 2009, 06:14
Scheinbar scheint ein Event "in Delphi" doch irgendwie anders gehandhabt zu werden, als ausschließlich über einen Zeiger.

Bzgl. einer Alternative würde ich daher vorschlagen einfach das Event über einen Wrapper zu kapseln:

Delphi-Quellcode:
type
  THAL_ObserverItem = class(TObject)
  private
    FEvent: TNotifyEvent;
  public
    constructor Create(iEvent: TNotifyEvent);
  public
    property Event: TNotifyEvent read FEvent write FEvent;
  end;

  THAL_Observer = class(TComponent)
  private
    FListSubscriber: TObjectList;
  protected
    function IndexOfEvent(_Event: TNotifyEvent): Integer;
  public
    procedure NotifySubscriber(_Sender: TObject);

    procedure RegisterSubscriber(_Event: TNotifyEvent);
    procedure UnRegisterSubscriber(_Event: TNotifyEvent);

    constructor Create(_Sender: TComponent); override;
    destructor Destroy; override;
  end;

[...]

{ THAL_ObserverItem }

constructor THAL_ObserverItem.Create(iEvent: TNotifyEvent);
begin
  inherited Create;
  FEvent := iEvent;
end;

{==============================================================================}

{$Region 'constructor THAL_Observer.Create(_Sender: TComponent);'}
constructor THAL_Observer.Create(_Sender: TComponent);
begin
  try
    inherited Create(_Sender);

    FListSubscriber := TObjectList.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;

function THAL_Observer.IndexOfEvent(_Event: TNotifyEvent): Integer;
var i: Integer;
begin
  for i := 0 to FListSubscriber.Count - 1 do
    if @THAL_ObserverItem(FListSubscriber[i]).Event = @_Event then
    begin
      Result := i;
      Exit;
    end;
  Result := -1;
end;

{$EndRegion}

{==============================================================================}

{$Region 'procedure THAL_Observer.NotifySubscriber(_Sender: TObject);'}
procedure THAL_Observer.NotifySubscriber(_Sender: TObject);
var
  i: integer;
begin
  try
    for i := 0 to FListSubscriber.Count - 1 do begin
      THAL_ObserverItem(FListSubscriber[i]).Event(Self);
    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 IndexOfEvent(_Event) = -1 then
      FListSubscriber.Add(THAL_ObserverItem.Create(_Event));

  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 IndexOfEvent(_Event) > -1 then begin
      FListSubscriber.Delete(IndexOfEvent(_Event));
    end;
  except
    on E: Exception do begin
    end;
  end;
end;
{$EndRegion}

{==============================================================================}
  Mit Zitat antworten Zitat
mkinzler
(Moderator)

Registriert seit: 9. Dez 2005
Ort: Heilbronn
39.851 Beiträge
 
Delphi 11 Alexandria
 
#4

Re: TNotifyEvent Objektbezogen in Variable speichern

  Alt 3. Sep 2009, 06:34
Ein Event ist eine Methodenreferenz, also schon ein Zeiger
Markus Kinzler
  Mit Zitat antworten Zitat
Benutzerbild von uligerhardt
uligerhardt

Registriert seit: 19. Aug 2004
Ort: Hof/Saale
1.735 Beiträge
 
Delphi 2007 Professional
 
#5

Re: TNotifyEvent Objektbezogen in Variable speichern

  Alt 3. Sep 2009, 08:39
Zitat von mkinzler:
Ein Event ist eine Methodenreferenz, also schon ein Zeiger
Zwei Zeiger - Zitat aus System.pas:
Delphi-Quellcode:
TMethod = record
  Code, Data: Pointer;
end;
Edit: Was ich so auf die Schnelle sehe, würde ich FListSubscriber durch ein array of TMethod o.ä. ersetzen.
Bitte vergessen - ist ja schon so - siehe THAL_ObserverItem.
Uli Gerhardt
  Mit Zitat antworten Zitat
berens

Registriert seit: 3. Sep 2004
431 Beiträge
 
Delphi 2010 Professional
 
#6

Re: TNotifyEvent Objektbezogen in Variable speichern

  Alt 3. Sep 2009, 09:02
@KrasserChecker: Das sieht verdammt gut aus und klappt auch soweit einwandfrei im Praxistest.

Das tolle ist so auch, dass sich nicht der Subscriber um den Wrapper kümmern muss, sondern nur der Observer selbst. Ich hätte (zumindest gestern Abend zu später Stunde) wohl angefangen, mit jedem Subscriber solch eine Wrapperkomponente zu erzeugen und dann am Observer anzumelden.


Wie gesagt, funktioniert Tadellos. Dickes Lob an KrasserChecker (und natürlich auch alle anderen )
  Mit Zitat antworten Zitat
Blup

Registriert seit: 7. Aug 2008
Ort: Brandenburg
1.429 Beiträge
 
Delphi 10.4 Sydney
 
#7

Re: TNotifyEvent Objektbezogen in Variable speichern

  Alt 3. Sep 2009, 14:04
Auch in der Funktion "IndexOfEvent" hat das @ nichts zu suchen.
  Mit Zitat antworten Zitat
berens

Registriert seit: 3. Sep 2004
431 Beiträge
 
Delphi 2010 Professional
 
#8

Re: TNotifyEvent Objektbezogen in Variable speichern

  Alt 23. Sep 2009, 09:15
Also hier müssen auch noch beide @ weg? Weil bisher hat es auch so gut geklappt.

Delphi-Quellcode:
function THAL_Observer.IndexOfEvent(_Event: TNotifyEvent): Integer;
var i: Integer;
begin
  for i := 0 to FListSubscriber.Count - 1 do
    if @THAL_ObserverItem(FListSubscriber[i]).Event = @_Event then
    begin
      Result := i;
      Exit;
    end;
  Result := -1;
end;
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 03:25 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz