I'm looking to create a singleton in Delphi. I've done this before using older versions of Delphi, and ended up using global variables (in the implementation section) and using initialization and finalization to take care of the instance. Also there was no way of preventing the user from creating an instance as you couldn't hide the standard constructor. I was wondering if any of the new features such as class constructors and destructors, and class variables (ok, not so new), perhaps generics, could help in creating a generic singleton class. I haven't managed to create something to my satisfaction yet.
views:
1141answers:
6For a singleton, you can override the NewInstance method. And use a class variable. You create the variable at first call and return the pointer to the class each other call.
You just need to find someting to destroy it in the end (probably using the finalize).
In Delphi 2010 by far, the best and safest way is to use class constructors. See here - read especially the paragraph called Improved encapsulation.
HTH.
I prefer to use interfaces when I need singletons and hide the implementation of the interface in the implementation section.
benefits
- Automatic destruction when the program terminates.
- No way to accidently create a TMySingleton.
drawbacks
- Someone might decide to implement IMySingleton on its own.
Note: I believe the use of Singletons should be kept to an absolute minimum. All in all, Singletons are little more than glorified global variables. If and when you start unit testing your code, they become a nuisance.
unit uSingleton;
interface
type
ISingleton = interface
['{8A449E4B-DEF9-400E-9C21-93DFA2D5F662}']
end;
function Singleton: ISingleton;
implementation
uses
SyncObjs;
type
TSingleton = class(TInterfacedObject, ISingleton);
var
Lock: TCriticalSection;
function Singleton: ISingleton;
const
_singleton: ISingleton = nil;
begin
if not Assigned(_singleton) then
begin
Lock.Acquire;
try
if not Assigned(_singleton) then
_singleton := TSingleton.Create();
finally
Lock.Release;
end;
end;
Result := _singleton;
end;
initialization
Lock := TCriticalSection.Create;
finalization
Lock.Free;
end.
If you just need a plain singleton, the simplest way is to use class constructors and class methods as suggested by plainth. But generics are very helpful if you need singletons with construction on demand (i.e. on first access).
The following code is taken from one of my utility units; it basically provides a generic singleton factory for Delphi 2009 onwards.
interface
type
{$HINTS OFF}
{ TSingletonInstance<> implements lazy creation, which is sometimes useful for avoiding
expensive initialization operations.
If you do not require lazy creation and you target only Delphi 2010 onwards, you should
use class constructors and class destructors instead to implement singletons. }
TSingletonInstance<T: class, constructor> = record
private
FGuard: IInterface;
FInstance: T;
function GetInstance: T;
function CreateInstance: TObject;
public
property Instance: T read GetInstance;
end;
{$HINTS ON}
TSingletonFactoryFunction = function: TObject of object;
{ Private symbols (which are in the interface section because of known limitations of generics) }
procedure _AllocateSingletonInstance (InstanceRecord: Pointer; Factory: TSingletonFactoryFunction);
implementation
{ TSingleton }
var
SingletonCriticalSection: TRTLCriticalSection;
type
TSingletonGuard = class (TInterfacedObject)
private
FSingletonInstance: TObject;
public
constructor Create (AInstance: TObject);
destructor Destroy; override;
end;
PUntypedSingletonInstance = ^TUntypedSingletonInstance;
TUntypedSingletonInstance = record
FGuard: IInterface;
FInstance: TObject;
end;
// TODO: is a lock required for multiple threads accessing a single interface variable?
procedure _AllocateSingletonInstance (InstanceRecord: Pointer; Factory: TSingletonFactoryFunction);
var
USI: PUntypedSingletonInstance;
begin
USI := PUntypedSingletonInstance (InstanceRecord);
EnterCriticalSection (SingletonCriticalSection);
if USI.FInstance = nil then
begin
USI.FInstance := Factory ();
USI.FGuard := TSingletonGuard.Create (USI.FInstance);
end;
LeaveCriticalSection (SingletonCriticalSection);
end;
constructor TSingletonGuard.Create (AInstance: TObject);
begin
FSingletonInstance := AInstance;
end;
destructor TSingletonGuard.Destroy;
begin
FSingletonInstance.Free;
inherited;
end;
function TSingletonInstance<T>.GetInstance: T;
var
Factory: TSingletonFactoryFunction;
begin
if FInstance = nil then
begin
Factory := Self.CreateInstance; // TODO: associate QC report
_AllocateSingletonInstance (@Self, Factory);
end;
Result := FInstance;
end;
function TSingletonInstance<T>.CreateInstance: TObject;
begin
Result := T.Create;
end;
initialization
InitializeCriticalSection (SingletonCriticalSection);
finalization
DeleteCriticalSection (SingletonCriticalSection);
Usage as follows:
type
TMySingleton = class
public
constructor Create;
class function Get: TMySingleton; static;
end;
var
MySingletonInstance: TSingletonInstance<TMySingleton>;
class function TMySingleton.Get: TMySingleton;
begin
Result := MySingletonInstance.Instance;
end;
It was possible to manage this by overriding the TRUE allocator and deallocator methods in Delphi, NewInstance and FreeInstance. Constructors and Destructors in Delphi only initialise and finalise respectively, they do not allocate or deallocate memory, so attempting to hide constructors was always a little misguided.
i.e. it was possible to allow free use of any and all constructors as long as you overrode NewInstance such that it only ever returned a reference to one single allocation of memory for the class.
But attempting to enforce a usage/behavioural pattern in a base class is a mistake imho. Not ALL patterns are or require specific classes to encapsulate the pattern.
In cases such as this you end up creating something that is needlessly complicated, and complication attracts errors in my experience and the object of the exercise then becomes trying to find flaws in the pattern implementation and then trying to implement safeguards against those flaws, rather than getting on with the practical job of work that the singleton class was supposed to perform.
It is far, FAR simpler and more effective to document the USAGE of the class.
Documentation as a technique for implementing this pattern has worked flawlessly for 15 years for the Application and Screen objects in the VCL, for example, not to mention countless other singletons that I've created in those years.
I prefer to create singleton class using a code generator. The problem with generic is that, all code is generated in memory, not in source file. It will increase the difficulty of debugging.