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