Delphi中的类/静态常量

Delphi中的类/静态常量

Class/Static Constants in Delphi

在Delphi中,我希望能够创建与类相关联的私有对象,并从该类的所有实例中访问它。 在Java中,我将使用:

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

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

(您可能已经猜到了……我知道我的Java语言,但我对Delphi还是陌生的。)

无论如何,我不想每次创建MyObject实例时都实例化一个新的MySharedObject,但我希望可以从MyObject的每个实例访问MySharedObject。 (实际上是日志记录,这促使我试图找出答案-我正在使用Log4D,并且我想将TLogLogger存储为具有日志记录功能的每个类的类变量。)

在Delphi中做这种事情的最巧妙的方法是什么?


这是使用类变量,类过程和初始化块的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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.

去年,Hallvard Vassbotn在博客中介绍了我为此编写的Delphi-hack,该文章分为两部分:

  • Hack#17:虚拟类变量,第一部分
  • Hack#17:虚拟类变量,第二部分
  • 是的,这是一本很长的书,但很有收获。

    总而言之,我将(已弃用的)称为vmtAutoTable的VMT条目作为变量重用。
    VMT中的该插槽可用于存储任何4字节的值,但如果要存储,则始终可以分配一条记录,其中包含所有希望的字段。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     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;

    请注意,该类变量可以从任何类实例中写入,因此您通常可以根据某些条件(记录器的类型等)将其设置在代码的其他位置。

    编辑:在该类的所有子代中也将相同。在其中一个子项中更改它,并且它对于所有后代实例都更改。
    您还可以设置默认实例处理。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     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;

    好吧,这不是美,但是在Delphi 7中可以正常工作:

    1
    2
    3
    4
    5
    6
    TMyObject = class
    pulic
        class function MySharedObject: TMySharedObject; // I'm lazy so it will be read only
    end;

    implementation

    ...

    1
    2
    3
    4
    5
    6
    7
    8
    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;

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


    您要查找的关键字是" class var"-这将在您的类声明中启动一个类变量。如果希望在块之后添加其他字段,则需要以" var"结尾(否则,该块可以由" private"," public"," procedure"等说明符结束)。例如

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
      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可以用TLogLogger或任何您喜欢的东西代替。


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

    • 首先,TLogLogger是否是线程安全的。是否可以从多个线程中调用同一TLogLogger而无需调用" syncronize"?即使是这样,以下内容仍然可能适用
    • 类变量是范围内线程还是真正的全局变量?
    • 如果类变量确实是全局变量,并且TLogLogger不是线程安全的,则可能最好使用单位全局线程变量来存储TLogLogger(就像我不喜欢以任何形式使用"全局"变量)一样,例如

    码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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;

    编辑:似乎类变量是全局存储的,而不是每个线程一个实例。有关详细信息,请参见此问题。


    对于我想做的事情(一个私有类常量),我可以想出的最简洁的解决方案(根据到目前为止的回答)是:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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.

    也许更多的面向对象将是这样的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    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.

    如果存在多个此类常量,那可能更有意义。


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

    这可能会产生误导。

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

    是的,另一种可能性是在模块的implementation部分中使用全局变量。

    这仅在全局或使用{$J+}语法(tnx Lars)打开编译器开关" Assignable Consts"时有效。


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

    要使其尽可能私密,请将其放在设备的implementation部分中。


    推荐阅读

      静态路由怎么设置|静态路由设置方法

      静态路由怎么设置|静态路由设置方法,,静态路由设置方法猫不能设置静态ip 只能拨号给你自动分配。但是路由器可以!dns,dhcp 不要去设置。只把

      静态路由表设置|静态路由设置方法

      静态路由表设置|静态路由设置方法,,静态路由设置方法第一步:打开无线设置,有WLAN设置,然后再确认手机选项,有高级设置,就看到下面有使用静态ip

      Python之可迭代对象、迭代器、生成器

      Python之可迭代对象、迭代器、生成器,迭代,生成器,一、概念描述可迭代对象就是可以迭代的对象,我们可以通过内置的iter函数获取其迭代器,可

      应用程序对象

      应用程序对象,,应用程序对象是一个应用程序级对象,用于在所有用户之间共享信息,并且在Web应用程序运行期间可以保存数据。 应用的性质: 方法

      Java创建对象的几种方式

      Java创建对象的几种方式,对象,方法,本文目录Java创建对象的几种方式java中几种创建对象的方式1Java中创建对象的集中方式有那些JAVA创建对

      电脑cpu静态功耗|CPU性能及功耗

      电脑cpu静态功耗|CPU性能及功耗,,CPU性能及功耗2021年手机功耗排行榜,第一就是美国的骁龙898处理器,性能没的说功耗肯定也很高,对于功耗高的