Delphi中的类/静态常量

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

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

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

(你可能已经猜到了……我知道我的Java,但我对Delphi相当新…)

无论如何,我不想在每次创建MyObject的实例时实例化一个新的MySharedObject,但我确实希望从MyObject的每个实例访问一个MySharedObject。(这实际上是日志刺激我试图弄清楚这一点-我正在使用Log4D,我想存储一个TLogLogger作为每个类的类变量,有日志功能。)

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

请先 登录 后评论

9 个回答

CL.

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

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

请先 登录 后评论
squadette

在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”被打开时才有效,无论是全局的还是with的 3. 语法(tnx佬司)。

请先 登录 后评论
Lars Fosdal
 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;
请先 登录 后评论
Graza

您正在寻找的关键字是“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可以被TLogLogger或其他你喜欢的东西替换。

请先 登录 后评论
Pierre-Jean Coudert

下面是我如何使用一个类变量,一个类过程和一个初始化块:

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.
请先 登录 后评论
PatrickvL

去年,Hallvard Vassbotn写了一篇关于我为此所做的delphi hack的博客,这篇文章分为两部分:

  1. 技巧17:虚拟类变量,第一部分
  2. Hack#17:虚拟类变量(第二部分

是啊,读起来很长,但很有价值。

总之,我重用了(已弃用的)VMT条目vmtAutoTable作为变量。VMT中的这个插槽可以用来存储任何4字节的值,但是如果您想要存储,您总是可以分配一个记录,其中包含您想要的所有字段。

请先 登录 后评论
MB.

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

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.

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

请先 登录 后评论
Graza

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

  • 第一个问题是TLogLogger是否线程安全。可以从多个线程调用同一个TLogLogger而不调用“同步”吗?即便如此,以下几点仍然适用
  • 类变量是作用域内线程还是真正的全局变量?
  • 如果类变量确实是全局的,而TLogLogger不是线程安全的,那么您最好使用一个单元全局线程变量来存储TLogLogger(因为我不喜欢以任何形式使用“全局”变量),例如

代码:

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;

编辑:似乎类变量是全局存储的,而不是每个线程一个实例。详情请参阅这个问题

请先 登录 后评论
Gedean Dias

好吧,这不是美,但在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;

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

请先 登录 后评论
  • 16 关注
  • 0 收藏,219 浏览
  • MB. 提出于 2022-10-15 00:42