在Delphi中,我希望能够创建一个与类关联的私有对象,并从该类的所有实例中访问它。在Java中,我使用:
public class MyObject {
private static final MySharedObject mySharedObjectInstance = new MySharedObject();
}
或者,如果MySharedObject需要更复杂的初始化,在Java中我可以在静态初始化程序块中实例化并初始化它。
(你可能已经猜到了......我知道我的Java,但我对Delphi很新......)
无论如何,我不希望每次创建MyObject实例时都实例化一个新的MySharedObject,但我确实希望可以从MyObject的每个实例访问MySharedObject。 (它实际上是日志记录,这促使我试图解决这个问题 - 我正在使用Log4D,我希望将TLogLogger存储为具有日志记录功能的每个类的类变量。)
在Delphi中做这样的事情最好的方法是什么?
答案 0 :(得分:15)
以下是我将如何使用类变量,类过程和初始化块:
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.
答案 1 :(得分:4)
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;
答案 2 :(得分:4)
去年,Hallvard Vassbotn在博客中写了一篇关于我为此做过的Delphi-hack,它成了两篇文章:
是的,这是一个很长的阅读,但非常有益。
总之,我重用了名为vmtAutoTable的(已弃用的)VMT条目作为变量。 VMT中的此插槽可用于存储任何4字节值,但如果要存储,则始终可以分配包含您希望的所有字段的记录。
答案 3 :(得分:2)
您要查找的关键字是“class var” - 这会在类声明中启动一个类变量块。如果您希望在其后包含其他字段,则需要使用“var”结束块(否则该块可能由“私有”,“公共”,“过程”等说明符结束)。例如
(编辑:我重新阅读了这个问题并将引用计数移到了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或您喜欢的任何内容。
答案 4 :(得分:2)
嗯,这不是美,但在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;
我正在使用它来构建单身对象。
答案 5 :(得分:1)
对于我想要做的事情(私有类常量),我能提出的最新解决方案(基于目前为止的响应)是:
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.
如果有多个这样的类常量,那可能会更有意义。
答案 6 :(得分:1)
我认为在提出“完美”解决方案之前需要回答两个问题。
代码:
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。
答案 7 :(得分:0)
在版本7之前,Delphi没有静态变量,你必须使用全局变量。
要将其设为私有,请将其放入设备的implementation
部分。
答案 8 :(得分:0)
在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)打开时,此方法才有效。