Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/unix/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi中的类/静态常量_Delphi - Fatal编程技术网

Delphi中的类/静态常量

Delphi中的类/静态常量,delphi,Delphi,在Delphi中,我希望能够创建一个与类关联的私有对象,并从该类的所有实例访问它。在Java中,我会使用: public class MyObject { private static final MySharedObject mySharedObjectInstance = new MySharedObject(); } 或者,如果myShareObject需要更复杂的初始化,在Java中,我可以在静态初始化程序块中实例化和初始化它 (你可能已经猜到了……我知道我的Java,但我对D

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

public class MyObject {
    private static final MySharedObject mySharedObjectInstance = new MySharedObject();
}
或者,如果myShareObject需要更复杂的初始化,在Java中,我可以在静态初始化程序块中实例化和初始化它

(你可能已经猜到了……我知道我的Java,但我对Delphi还比较陌生……)

无论如何,我不希望每次创建MyObject实例时都实例化一个新的MySharedObject,但我确实希望可以从MyObject的每个实例访问MySharedObject。(事实上,日志记录促使我尝试弄清楚这一点——我正在使用Log4D,我想为每个具有日志记录功能的类存储一个TLogLogger作为类变量。)


在Delphi中这样做的最简洁的方法是什么?

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


要使其尽可能私有,请将其放入单元的
实现部分。

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

这可能有点误导

procedure TForm1.Button1Click(Sender: TObject) ;
const
   clicks : Integer = 1; //not a true constant
begin
  Form1.Caption := IntToStr(clicks) ;
  clicks := clicks + 1;
end;
是的,另一种可能是在模块的
实现部分使用全局变量


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

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

编辑:在类的所有子体中也将相同。在其中一个子实例中更改它,所有子实例都会更改。 您还可以设置默认实例处理

 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;

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

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

TMyClass=class(TObject)
严格保密
类变量
FMySharedObjectRefCount:整数;
FMySharedObject:TMySharedObjectClass;
变量
FOtherNonClassField1:整数;
函数GetMySharedObject:TMySharedObjectClass;
公众的
构造函数创建;
毁灭者毁灭;推翻
属性MySharedObject:TMySharedObjectClass读取GetMySharedObject;
结束;
{TMyClass}
构造函数TMyClass.Create;
开始
如果未分配(FMySharedObject),则
FMySharedObject:=TMySharedObjectClass.Create;
公司(FMySharedObjectRefCount);
结束;
销毁程序TMyClass.Destroy;
开始
Dec(FMySharedObjectRefCount);
如果(FMySharedObjectRefCount<1),则
FreeAndNil(FMySharedObject);
继承;
结束;
函数TMyClass.GetMySharedObject:TMySharedObjectClass;
开始
结果:=FMySharedObject;
结束;

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

以下是我将如何使用类变量、类过程和初始化块来完成此操作:

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的博客,文章分为两部分:

  • 是的,这是一本很长的书,但很值得一读

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

    对于我想做的事情(一个私有类常量),我能想出的最简单的解决方案(基于到目前为止的响应)是:

    也许稍微面向对象一点会是这样的:

    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.
    

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

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

    • 第一个问题是TLogLogger是否是线程安全的。可以从多个线程调用同一个TLogLogger而不调用“syncronize”吗?即使如此,以下内容仍然适用
    • 类变量是线程范围内的还是真正的全局变量
    • 如果类变量是真正的全局变量,而TLogLogger不是线程安全的,那么最好使用一个单元全局threadvar来存储TLogLogger(尽管我不喜欢以任何形式使用“全局”变量),例如
    代码:


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

    好吧,这不是美,但在Delphi 7中效果很好:

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


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

    在Delphi7之前都是这样。更高版本有类变量(请参见其他答案)。我可以看出,如果您在类外共享对象,这样做可能有意义,但是对于一个只在类的实例之间共享的对象来说,这是大量的脚手架代码。只有当编译器开关AssignableConsts打开时,这才有效。如果需要一个私有字段,则需要SetLogger过程。我同意终结程序的意见。我认为应该可以从初始值设定项设置私有字段,因为私有字段可以从单元内部访问。它必须是“严格私有”的,才能仅从类内访问。在释放之前,您不需要调用assigned-free为您执行此操作。您还可以使用类构造函数:而不是初始化/终结将内容放在init部分有一些缺点:甚至i
    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.
    
    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;
    
    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;