AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Projekte Singleton in Delphi
Thema durchsuchen
Ansicht
Themen-Optionen

Singleton in Delphi

Ein Thema von Stevie · begonnen am 20. Sep 2010 · letzter Beitrag vom 21. Sep 2010
Antwort Antwort
Seite 2 von 3     12 3      
Benutzerbild von Stevie
Stevie
Registriert seit: 12. Aug 2003
Ein kleines Nebenprodukt was beim Experimentieren in Delphi 2010 entstanden ist.
Ich weiß, Singletons sind böse...

Trotzdem hier mal eine ab Delphi 2010 funktionierene (evtl auch Delphi 2009 oder eher) Unit, die aus einer normalen Klasse ein Singleton macht, wovon weder ein zweites mal eine Instanz erzeugt noch die bestehende Instanz freigegeben werden kann. Hab es bisher im kleinen Stil getestet und dachte, evtl interessiert sich hier der eine oder andere für son krankes Zeug

Die Benutzung ist denkbar einfach, nachfolgend kurz, was so alles funktioniert:
Delphi-Quellcode:
uses
  Singleton;

type
  TFoo = class
  private
    FText: string;
  public
    property Text: string read FText write FText;
  end;

type
  TFooSingleton = Singleton<TFoo>;

var
  FooSingleton: Singleton<TFoo>;
  Foo: TFoo;
begin
  Foo := FooSingleton;
  TFooSingleton.Instance.Text := 'Hello Foo';
  ShowMessage(Foo.Text);
end;
Die Instanz der Klasse wird beim Starten des Programms (ich vermute in Initialization Reihenfolge der Units, genau hab ich es noch nicht getestet) erstellt und beim Beenden (Finalization der Unit Singleton.pas) wieder freigegeben. Der VMT Hack wird vorher schon (Vermutung: finalization der Unit wo der jeweilige Singleton benutzt wird) entfernt.
Angehängte Dateien
Dateityp: pas Singleton.pas (3,7 KB, 101x aufgerufen)
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight
 
Benutzerbild von himitsu
himitsu

 
Delphi 12 Athens
 
#11
  Alt 21. Sep 2010, 12:24
OK, dann hier mal meine Gedanken zu einem SingletonPattern

die Basisklasse für Delphi 2009 und davor:
Delphi-Quellcode:
type
  TSingleton = class(TObject)
  private
    fIsInitialized: Boolean;
    fAllowFree:     Boolean;
    fIsSingelton:   Boolean;
    class var fSingleton: TSingleton;
    class procedure DoFree;
  protected
    property isInitialized: Boolean read fIsInitialized;  // to see whether the constructor must be executed (in contructors)
    property AllowFree:     Boolean read fAllowFree;      // to detect whether the object is released (in destructors)
    property isSingelton:   Boolean read fIsSingelton;    // note: not yet available in constructor
  public
    class function NewInstance: TObject; override;
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
    procedure FreeInstance; override;
  end;

class procedure TSingleton.DoFree;
begin
  if Assigned(fSingleton) then
    fSingleton.fAllowFree := True;
  fSingleton.Free;
end;

class function TSingleton.NewInstance: TObject;
begin
  if Assigned(fSingleton) then
    Result := fSingleton
  else
    Result := inherited;
end;

procedure TSingleton.AfterConstruction;
begin
  inherited;
  fIsSingelton := not Assigned(InterlockedCompareExchangePointer(Pointer(fSingleton), Pointer(Self), nil));
  fIsInitialized := True;
  if not fIsSingelton then fAllowFree := True;
end;

procedure TSingleton.BeforeDestruction;
begin
  if fAllowFree then
    inherited;
end;

procedure TSingleton.FreeInstance;
begin
  if fAllowFree then
    inherited;
end;

class destructor TSingleton.DestroyClass;
begin
  if Assigned(fSingleton) then
    fSingleton.fAllowFree := True;
  fSingleton.Free;
end;

initialization

finalization
  TSingleton.DoFree;

end.
die Basisklasse ab Delphi 2010 (die ältere Version geht aber auch noch):
Delphi-Quellcode:
type
  TSingleton = class(TObject)
  private
    fIsInitialized: Boolean;
    fAllowFree:     Boolean;
    fIsSingelton:   Boolean;
    class var fSingleton: TSingleton;
  protected
    property isInitialized: Boolean read fIsInitialized;  // to see whether the constructor must be executed (in contructors)
    property AllowFree:     Boolean read fAllowFree;      // to detect whether the object is released (in destructors)
    property isSingelton:   Boolean read fIsSingelton;    // note: not yet available in constructor
  public
    class function NewInstance: TObject; override;
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
    procedure FreeInstance; override;
    class destructor DestroyClass;
  end;

class function TSingleton.NewInstance: TObject;
begin
  if Assigned(fSingleton) then
    Result := fSingleton
  else
    Result := inherited;
end;

procedure TSingleton.AfterConstruction;
begin
  inherited;
  fIsSingelton := not Assigned(InterlockedCompareExchangePointer(Pointer(fSingleton), Pointer(Self), nil));
  fIsInitialized := True;
  if not fIsSingelton then fAllowFree := True;
end;

procedure TSingleton.BeforeDestruction;
begin
  if fAllowFree then
    inherited;
end;

procedure TSingleton.FreeInstance;
begin
  if fAllowFree then
    inherited;
end;

class destructor TSingleton.DestroyClass;
begin
  if Assigned(fSingleton) then
    fSingleton.fAllowFree := True;
  fSingleton.Free;
end;
und eine Beispielklasse:
Delphi-Quellcode:
type
  TMyClass = class(TSingleton)
    Value: String;
    constructor Create;
    destructor Destroy; override;
  end;

constructor TMyClass.Create;
begin
  if not isInitialized then
  begin
    inherited;
    ////////////////////

    ShowMessage('Ich wurde erstellt');

    ////////////////////
  end;
end;

destructor TMyClass.Destroy;
begin
  if AllowFree then
  begin
    ////////////////////

    //ShowMessage('ich werde jetzt freigegeben');
    // wird nicht mehr angezeigt, nachdem die VCL beendet wurde
    MessageBox(0, 'ich werde jetzt freigegeben', '', 0);

    ////////////////////
    inherited;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  S: TMyClass;
begin
  S := TMyClass.Create;
  S.Value := 'test';
  S.Free;

  S := TMyClass.Create;
  ShowMessage('mein Wert ist: ' + S.Value);
end;

Geändert von himitsu (21. Sep 2010 um 12:44 Uhr)
  Mit Zitat antworten Zitat
Alt 21. Sep 2010, 12:29     Erstellt von himitsu
Dieser Beitrag wurde von fkerber gelöscht. - Grund: Doppelposting
Benutzerbild von Stevie
Stevie

 
Delphi 10.1 Berlin Enterprise
 
#12
  Alt 21. Sep 2010, 12:57
Wie schon erwähnt, immernoch das gleiche Problem, du musst eine Klasse, die du als Singleton benutzen willst, von TSingleton ableiten -> Abhängigkeit.
Du kannst keine beliebige (wie schon erwähnt, in Theorie, habs noch nicht mit mehr als TFoo und TBar getestet) Klassen in Singletons umwandeln, ich schon.
Stefan
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

 
Delphi 12 Athens
 
#13
  Alt 21. Sep 2010, 13:04
Joar, das Singleton muß hier quasi an der Spitze stehen.
Leider bieten die Generics es nicht an, daß man damit die Basisklasse setzen kann.
Delphi-Quellcode:
TSingleton<Base: class> = class(Base)
  ...
end;
Du kannst es aber manuell selber machen, indem du TObjekt bei TSingleton abänderst.
Man könnte ja zumindestens eine Codevorlage daraus basteln.
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

 
Delphi 10.1 Berlin Enterprise
 
#14
  Alt 21. Sep 2010, 13:21
Joar, das Singleton muß hier quasi an der Spitze stehen.
Leider bieten die Generics es nicht an, daß man damit die Basisklasse setzen kann.
Delphi-Quellcode:
TSingleton<Base: class> = class(Base)
  ...
end;
Du kannst es aber manuell selber machen, indem du TObjekt bei TSingleton abänderst.
Man könnte ja zumindestens eine Codevorlage daraus basteln.
Das ist doch genau das, was ich gemacht habe
Stefan
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

 
Delphi 12 Athens
 
#15
  Alt 21. Sep 2010, 13:26
Das ist doch genau das, was ich gemacht habe
Nein,

du hast quasi dieses
Delphi-Quellcode:
TSingleton<T> = class
  ...
  class property Instance: T read FInstance;
  ...
end;
gemacht, aber nicht jenes
Delphi-Quellcode:
TSingleton<T> = class<T>
  ...
end;
.


Also du hast eine Klasse in einer anderen Klasse/Record verpackt.
(wobei man dort eben auch noch aufpassen muß, daß man dieses gekapselte Objekt nicht extern freigibt)

Bei mir und wenn dieser Generic so ginge, würde das Objekt von dem Singleton abgeleitet und hätte dann in sich selber diese Funktionalität aufgenommen.

Oder man leitet den Singleton von der gewünschten Klasse ab und baut dieses Verhalten dann nachträglich ein (hierfür muß man aber zusätzlich noch alle Konstruktoren überschreiben/verdecken und mit Konstrukoren besetzen, welche der dem vorhandenen TSingleton.Create entsprechen).

Geändert von himitsu (21. Sep 2010 um 13:34 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

 
Delphi 10.1 Berlin Enterprise
 
#16
  Alt 21. Sep 2010, 14:02
Also du hast eine Klasse in einer anderen Klasse/Record verpackt.
(wobei man dort eben auch noch aufpassen muß, daß man dieses gekapselte Objekt nicht extern freigibt)
Und genau das hab ich ja verhindert. Ich habe quasi soweit mit Delphi möglich
Delphi-Quellcode:
TSingleton<T> = class<T>
  ...
end;
gebaut, denn genau das, was den Singleton ausmacht ist, nämlich, dass nur einmal eine Instanz erzeugt wird und verhindert wird, diese freizugeben, ist gegeben.
Stefan
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

 
Delphi 12 Athens
 
#17
  Alt 21. Sep 2010, 15:05
Irgendwie wird in meinem D2010 dein Class-Constructor nicht aufgerufen.
Folglich bleibt die Instanz immer nil und beim Zugriff darauf knallt es dann immer.
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

 
Delphi 10.1 Berlin Enterprise
 
#18
  Alt 21. Sep 2010, 15:48
Irgendwie wird in meinem D2010 dein Class-Constructor nicht aufgerufen.
Folglich bleibt die Instanz immer nil und beim Zugriff darauf knallt es dann immer.
Zeig ma bitte, wie du den Singleton erstellt hast.
Stefan
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

 
Delphi 12 Athens
 
#19
  Alt 21. Sep 2010, 15:56
Erst hatte ich was Eigenes versucht, aber auch mit deinem Beispielcode aus'm Post #1 (mit TStringList statt TFoo) geht's nicht.

Geändert von himitsu (21. Sep 2010 um 15:58 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

 
Delphi 10.1 Berlin Enterprise
 
#20
  Alt 21. Sep 2010, 16:05
Erst hatte ich was Eigenes versucht, aber auch mit deinem Beispielcode aus'm Post #1 (mit TStringList statt TFoo) geht's nicht.
Konsolenanwendung? Hab gerade gemerkt, dass dort was schief läuft, schau ich mir nachher mal zu Hause an. In ner VCL Forms Anwendung läufts bei mir.
Stefan
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 3     12 3      


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 10:52 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