代码之家  ›  专栏  ›  技术社区  ›  MB.

Delphi中的类/静态常量

  •  14
  • MB.  · 技术社区  · 16 年前

    在Delphi中,我希望能够创建一个与类关联的私有对象,并从该类的所有实例访问它。在爪哇,我会用:

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

    或者,如果MySyrdObjor需要更复杂的初始化,在Java中,我可以在静态初始化程序块中实例化和初始化它。

    (你可能已经猜到了……我知道我的Java,但是我对Delphi很陌生…

    无论如何,我不想每次创建myObject实例时都实例化一个新的mySharedObject,但我确实希望可以从myObject的每个实例访问mySharedObject。(实际上是日志记录促使我尝试解决这个问题——我正在使用log4d,我想将tlogogogger存储为具有日志记录功能的每个类的类变量。)

    在德尔福,最整洁的方法是什么?

    9 回复  |  直到 14 年前
        1
  •  15
  •   Pierre-Jean Coudert    16 年前

    下面是我将如何使用类变量、类过程和初始化块来实现这一点:

    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.
    
        2
  •  4
  •   Lars Fosdal    16 年前
     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;
    

    注意,这个类变量在任何类实例中都是可写的,因此您可以在代码中的其他地方设置它,通常基于某些条件(记录器类型等)。

    编辑:在类的所有子代中也是相同的。在其中一个子实例中更改它,并对所有子实例进行更改。 还可以设置默认实例处理。

     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;
    
        3
  •  4
  •   PatrickvL    16 年前

    去年,Hallvard Vassbotn写了一篇关于Delphi黑客的博客,文章分为两部分:

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

    是的,这本书读得很长,但很有价值。

    总之,我将名为vmtotable的(不推荐使用的)vmt条目重新用作变量。 vmt中的这个槽可以用来存储任何4字节的值,但是如果您想要存储,您可以始终为一个记录分配您想要的所有字段。

        4
  •  2
  •   Graza    16 年前

    您要查找的关键字是“class var”-这会在类声明中启动一个类变量块。如果希望在块后面包含其他字段,则需要以“var”结束块(否则块可能以“private”、“public”、“procedure”等说明符结束)。如

    (编辑:我重新阅读了问题并将引用计数移动到tmyclass中-因为如果它来自其他人的库,您可能无法编辑要共享的tmysharedObjectClass类)

      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;
    

    请注意,上面不是线程安全的,可能有更好的引用计数方法(例如使用接口),但这是一个简单的示例,应该可以让您开始。注意,tmysharedObjectClass可以由tlogogger或您喜欢的任何内容替换。

        5
  •  2
  •   Gedean Dias    15 年前

    好吧,这不是美丽,但在Delphi7中效果很好:

    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;
    

    我正在使用它来构建单例对象。

        6
  •  1
  •   MB.    16 年前

    对于我想要做的(一个私有类常量),我能想到的最新解决方案(基于目前的响应)是:

    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.
    

    也许更面向对象的方法是:

    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.
    

    如果有多个这样的类常量,这可能更有意义。

        7
  •  1
  •   Community Dunja Lalic    7 年前

    在你提出一个“完美”的解决方案之前,我认为有两个问题需要回答。

    • 首先,是tlogogogger是否是线程安全的。可以从多个线程调用同一个tloglogger吗 没有 呼叫“同步”?即使如此,以下内容仍然适用
    • 类变量是在作用域中线程还是真正的全局变量?
    • 如果类变量是真正的全局变量,并且tlogogger不是线程安全的,那么最好使用UnitGlobalThreadvar来存储tlogogger(就像我不喜欢以任何形式使用“global”vars一样),例如

    代码:

    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;
    

    编辑:类变量似乎是全局存储的,而不是每个线程一个实例。见 this question 详情。

        8
  •  0
  •   squadette    16 年前

    在Delphi中,静态变量实现为 变量类型常量 :)

    这可能有些误导。

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

    是的,另一种可能是在 implementation 模块的一部分。

    只有当编译器开关“assignable consts”打开、全局打开或使用 {$J+} 语法(tnx lars)。

        9
  •  0
  •   CL.    16 年前

    在版本7之前,Delphi没有静态变量,您必须使用全局变量。

    为了使它尽可能私密,把它放在 implementation 你的部门。