Class/Static Constants in Delphi

前端 未结 9 739
粉色の甜心
粉色の甜心 2021-02-05 12:06

In Delphi, I want to be able to create an private object that\'s associated with a class, and access it from all instances of that class. In Java, I\'d use:

pub         


        
相关标签:
9条回答
  • 2021-02-05 12:29

    Last year, Hallvard Vassbotn blogged about a Delphi-hack I had made for this, it became a two-part article:

    1. Hack#17: Virtual class variables, Part I
    2. Hack#17: Virtual class variables, Part II

    Yeah, it's a long read, but very rewarding.

    In summary, I've reused the (deprecated) VMT entry called vmtAutoTable as a variable. This slot in the VMT can be used to store any 4-byte value, but if you want to store, you could always allocate a record with all the fields you could wish for.

    0 讨论(0)
  • 2021-02-05 12:29

    Before version 7, Delphi didn't have static variables, you'd have to use a global variable.

    To make it as private as possible, put it in the implementation section of your unit.

    0 讨论(0)
  • 2021-02-05 12:30

    Well, it's not beauty, but works fine 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;
    

    I'm curently using it to build singletons objects.

    0 讨论(0)
  • 2021-02-05 12:32

    The keywords you are looking for are "class var" - this starts a block of class variables in your class declaration. You need to end the block with "var" if you wish to include other fields after it (otherwise the block may be ended by a "private", "public", "procedure" etc specifier). Eg

    (Edit: I re-read the question and moved reference count into TMyClass - as you may not be able to edit the TMySharedObjectClass class you want to share, if it comes from someone else's library)

      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;
    

    Please note the above is not thread-safe, and there may be better ways of reference-counting (such as using Interfaces), but this is a simple example which should get you started. Note the TMySharedObjectClass can be replaced by TLogLogger or whatever you like.

    0 讨论(0)
  • 2021-02-05 12:33

    In Delphi static variables are implemented as variable types constants :)

    This could be somewhat misleading.

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

    And yes, another possibility is using global variable in implementation part of your module.

    This only works if the compiler switch "Assignable Consts" is turned on, globally or with {$J+} syntax (tnx Lars).

    0 讨论(0)
  • 2021-02-05 12:36
     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;
    

    Note that this class variable will be writable from any class instance, hence you can set it up somewhere else in the code, usually based on some condition (type of logger etc.).

    Edit: It will also be the same in all descendants of the class. Change it in one of the children, and it changes for all descendant instances. You could also set up default instance handling.

     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;
    
    0 讨论(0)
提交回复
热议问题