When your day work involves programming in Delphi and you have programmed in a language such as C++ or C# which allow static class you sometimes miss them as they are an useful resource.
A static class is, simply put, a class that provides some methods, properties and even fields but which doesn't need an instance to be used, that is, you don't need to create an instance of the class in order to call its methods or access its properties.
Actually, in C# for example, this separation reach the method, field and property level, that is, it is possible to define some methods to be static while other ones are not, and so such methods may be called without having to instantiate the class. In C# that looks more or less like this:
public MyStaticClass()
{
m_value = 0;
}
public int Value(){ return m_value; }
public static int ModDivision(int numerator, int denominator)
{
int result = numerator;
while (result < denominator)
{
result = result - denominator;
}
return result;
}
}
In this way the class has two methods one of which is static and one which is not, so that we can do call it as invocation:
but we could not invoke the Value function without creating an instance of the class.
Unfortunately delphi implicitly does not provide any method for working with static classes, it provides a similar mechanism, which si named class procedure or class function that provides similar functionality but require the existence of a declaration of the appropriate type variable (see code in the appendix).
But we can simulate this functionality with a simple trick using two sections of a Delphi unit, the initialization section and the finalization section
The trick is to declare the class normally, then declare a private variable to hold the class, create a function that allows access to that class and finally instanstiate the class. Let's see example:
function MyStaticClass: TMyStaticClass;
implementation
var cInstance : TMyStaticClass; // Variable which holds the class instance
refCount : integer; // Reference to the variable
// Access is implemented as a function so instance cannot be overwritten,
function MyStaticClass : TMyStaticClass;
begin
if Assigned(cInstance) then
result := cInstance
else
raise EInvalidPointer.Create('Invalid instance reference');
end;
{ Method implementation for the class here }
{ ************************************************************* }
initialization
begin
// Make sure Application object is updated
Application.ProcessMessages;
// if the application is not closing (yep, that may happen)
if not Application.Terminated then
begin
if (refCount = 0) then
begin
if cInstance = nil then
begin
// Create the instance if not already created
cInstance := TMiClaseEstatica.Create;
end;
end;
Inc(refCount); // Increase reference count
end;
end;
finalization
begin
// Decrease reference count
Dec(RefCount);
// If reference count reach zero, free
if RefCount = 0 then
begin
cInstance.Free;
cInstance := nil;
end;
end;
In the previous code, the initialization and finalization sections are the key. When delphi starts loading the application it reads the .dpr and runs all the units specified in the uses clause and "process" these units.
The process is done by delphi (roughly) in two steps:
For practical purposes this means that any unit that uses our class will execute the initialization section before executing any code and therefore must already have created an instance of the class to which we can access.
type MyNotSoStaticClass = class
private
m_val : integer := 43;
public
class function GetValue : integer;
end;
implementation
function MyNotSoStaticClass .GetValue : integer;
begin
result := m_val;
end;
procedure TMainForm.Button1Click(Sender : TObject);
var MyClass : MyNotSoStaticClass;
begin
ShowMessage(IntToStr(MiClase.GetValue));
end;
So as you see, with class functions it is necessary to make the declaration var MyClass: MyNotSoStaticClass to call the function which, although correct, is that a more cumbersome method than static classes.