Frage

In Delphi, Ich will ein privates Objekt erstellen können, die mit einer Klasse zugeordnet ist und Zugriff auf sie von allen Instanzen dieser Klasse. In Java, würde ich verwenden:

public class MyObject {
    private static final MySharedObject mySharedObjectInstance = new MySharedObject();
}

Oder, wenn MySharedObject komplizierte Initialisierung benötigt, in Java konnte ich es in einem statischen Initialisierungsblocks instanziiert und initialisiert wird.

(Sie ahnen es ... Ich weiß, dass mein Java, aber ich bin ziemlich neu in Delphi ...)

Wie auch immer, ich will keine neues MySharedObject jedes Mal, wenn ich eine Instanz von MyObject schaffen instanziiert, aber ich ein MySharedObject will von jeder Instanz von MyObject zugänglich sein. (Es ist eigentlich die Protokollierung das hat mich angespornt, um zu versuchen, das herauszufinden. - Ich bin mit Log4D und ich mag für jede Klasse eine TLogLogger als Klassenvariable speichern, die Funktionalität Protokollierung)

Was ist der sauberste Weg, so etwas wie dies in Delphi zu tun?

War es hilfreich?

Lösung

Hier ist, wie ich tun werde, dass eine Klassenvariable, eine Klasse Verfahren und eine Initialisierungsbaustein mit:

unit MyObject;

interface

type

TMyObject = class
   private
     class var FLogger : TLogLogger;
   public
     class procedure SetLogger(value:TLogLogger);
     class procedure FreeLogger;
   end;

implementation

class procedure TMyObject.SetLogger(value:TLogLogger);
begin
  // sanity checks here
  FLogger := Value;
end;

class procedure TMyObject.FreeLogger;
begin
  if assigned(FLogger) then 
    FLogger.Free;
end;

initialization
  TMyObject.SetLogger(TLogLogger.Create);
finalization
  TMyObject.FreeLogger;
end.

Andere Tipps

 TMyObject = class
    private
      class var FLogger : TLogLogger;
      procedure SetLogger(value:TLogLogger);
      property Logger : TLogLogger read FLogger write SetLogger;
    end;

procedure TMyObject.SetLogger(value:TLogLogger);
begin
  // sanity checks here
  FLogger := Value;
end;

Beachten Sie, dass dieser Klassenvariable aus jeder Klasse Instanz beschreibbar sein wird, daher können Sie es irgendwo anders im Code festgelegt, in der Regel basierend auf einer Bedingung (Art des Logger usw.).

Edit: Es wird auch das gleiche in allen Nachkommen der Klasse sein. Ändern Sie es in einem der Kinder, und es ändert sich für alle Nachkommen Instanzen. Sie können auch Standardinstanz Handhabung einrichten.

 TMyObject = class
    private
      class var FLogger : TLogLogger;
      procedure SetLogger(value:TLogLogger);
      function GetLogger:TLogLogger;
      property Logger : TLogLogger read GetLogger write SetLogger;
    end;

function TMyObject.GetLogger:TLogLogger;
begin
  if not Assigned(FLogger)
   then FLogger := TSomeLogLoggerClass.Create;
  Result := FLogger;
end;

procedure TMyObject.SetLogger(value:TLogLogger);
begin
  // sanity checks here
  FLogger := Value;
end;

Im vergangenen Jahr Hallvard Vassbotn über einen Delphi-Hack gebloggt ich dafür gemacht hatte, wurde ein zweiteiliger Artikel:

  1. Hack # 17: Virtuelle Klassenvariablen Teil I
  2. Hack # 17: Virtuelle Klassenvariablen Teil II

Ja, es ist eine lange lesen, aber sehr lohnend.

Insgesamt habe ich wieder verwendet den (veraltet) VMT Eintrag vmtAutoTable als Variable bezeichnet. Dieser Schlitz in der VMT verwendet werden, kann eine beliebige 4-Byte-Wert zu speichern, aber wenn Sie speichern möchten, könnten Sie immer einen Datensatz mit allen Feldern zuordnen Sie wünschen kann.

Die Keywords, die Sie suchen sind „class var“ - so beginnt ein Block von Klassenvariablen in der Klassendeklaration. Sie müssen den Block mit „var“ beenden, wenn Sie möchten, andere Felder enthalten, nachdem sie (sonst kann der Block durch eine „private“, „public“, „Verfahren“ etc Spezifizierer beendet werden). Eg

(Edit: Ich lese die Frage und bewegte Referenzzähler in TMyClass - wie Sie nicht in der Lage sein können, die TMySharedObjectClass Klasse, die Sie teilen mögen, zu bearbeiten, wenn es von jemandem anderer Bibliothek kommt)

  TMyClass = class(TObject)
  strict private
    class var
      FMySharedObjectRefCount: integer;
      FMySharedObject: TMySharedObjectClass;
    var
    FOtherNonClassField1: integer;
    function GetMySharedObject: TMySharedObjectClass;
  public
    constructor Create;
    destructor Destroy; override;
    property MySharedObject: TMySharedObjectClass read GetMySharedObject;
  end;


{ TMyClass }
constructor TMyClass.Create;
begin
  if not Assigned(FMySharedObject) then
    FMySharedObject := TMySharedObjectClass.Create;
  Inc(FMySharedObjectRefCount);
end;

destructor TMyClass.Destroy;
begin
  Dec(FMySharedObjectRefCount);
  if (FMySharedObjectRefCount < 1) then
    FreeAndNil(FMySharedObject);

  inherited;
end;

function TMyClass.GetMySharedObject: TMySharedObjectClass;
begin
  Result := FMySharedObject;
end;

Bitte beachten Sie die oben ist nicht Thread-sicher, und es können bessere Möglichkeiten der Referenzzählung sein (wie zB Schnittstellen), aber dies ist ein einfaches Beispiel, das Sie begonnen erhalten sollen. Notieren Sie sich die TMySharedObjectClass kann durch TLogLogger ersetzt werden oder was auch immer Sie mögen.

Nun, es ist nicht Schönheit, sondern funktioniert in Delphi 7:

TMyObject = class
pulic
    class function MySharedObject: TMySharedObject; // I'm lazy so it will be read only
end;

implementation

...

class function MySharedObject: TMySharedObject;
{$J+} const MySharedObjectInstance: TMySharedObject = nil; {$J-} // {$J+} Makes the consts writable
begin
    // any conditional initialization ...
   if (not Assigned(MySharedObjectInstance)) then
       MySharedObjectInstance = TMySharedOject.Create(...);
  Result := MySharedObjectInstance;
end;

Ich bin mit curently es Singletons Objekte zu bauen.

Für das, was ich (eine private Klasse konstant) tun will, die sauberste Lösung, die ich mit oben kommen kann (basierend auf den Antworten bisher) ist:

unit MyObject;

interface

type

TMyObject = class
private
  class var FLogger: TLogLogger;
end;

implementation

initialization
  TMyObject.FLogger:= TLogLogger.GetLogger(TMyObject);
finalization
  // You'd typically want to free the class objects in the finalization block, but
  // TLogLoggers are actually managed by Log4D.

end.

Vielleicht ein wenig mehr objektorientierte würde wie etwas sein:

unit MyObject;

interface

type

TMyObject = class
strict private
  class var FLogger: TLogLogger;
private
  class procedure InitClass;
  class procedure FreeClass;
end;

implementation

class procedure TMyObject.InitClass;
begin
  FLogger:= TLogLogger.GetLogger(TMyObject);
end;

class procedure TMyObject.FreeClass;
begin
  // Nothing to do here for a TLogLogger - it's freed by Log4D.
end;

initialization
  TMyObject.InitClass;
finalization
  TMyObject.FreeClass;

end.

Das könnte mehr Sinn machen, wenn es mehr solche Klassenkonstanten waren.

Zwei Fragen denke ich, dass müssen beantwortet werden, bevor Sie mit einer „perfekten“ Lösung zu kommen ..

  • Die erste ist, ob TLogLogger ist Thread-sicher. Kann das gleiche TLogLogger aufgerufen werden von mehreren Threads ohne Anrufe auf „Synchronize“? Selbst wenn dies der Fall, kann folgendes gelten weiterhin
  • Sind Klassenvariablen Thread-in-Bereich oder wirklich global?
  • Wenn Klassenvariablen wirklich global sind, und TLogLogger ist nicht Thread-sicher, dass Sie vielleicht am besten mit einem anteil globalem threadvar verwenden, um die TLogLogger zu speichern (so viel wie Ich mag es nicht „global“ Vars in irgendeiner Form verwenden) , zB

Code:

interface
type
  TMyObject = class(TObject)
  private
    FLogger: TLogLogger; //NB: pointer to shared threadvar
  public
    constructor Create;
  end;
implementation
threadvar threadGlobalLogger: TLogLogger = nil;
constructor TMyObject.Create;
begin
  if not Assigned(threadGlobalLogger) then
    threadGlobalLogger := TLogLogger.GetLogger(TMyObject); //NB: No need to reference count or explicitly free, as it's freed by Log4D
  FLogger := threadGlobalLogger;
end;

Edit: Es scheint, dass Klassenvariablen global gespeichert werden, anstatt eine Instanz pro Thread. Siehe diese Frage .

In Delphi statischen Variablen sind implementiert als Variablentypen Konstanten :)

Dies könnte etwas irreführend sein.

procedure TForm1.Button1Click(Sender: TObject) ;
const
   clicks : Integer = 1; //not a true constant
begin
  Form1.Caption := IntToStr(clicks) ;
  clicks := clicks + 1;
end;

Und ja, ein andere Möglichkeit globalen Variable in implementation Teil des Moduls verwendet wird.

Dies funktioniert nur, wenn der Compiler-Schalter "zuweisbare consts" eingeschaltet ist, global oder mit {$J+} Syntax (tnx Lars).

Vor der Version 7, Delphi keine statischen Variablen haben, würden Sie haben eine globale Variable verwenden.

Um es so privat wie möglich zu machen, es in dem implementation Abschnitt des Geräts setzen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top