How can I automatically initialize Delphi entries?

To initialize Delphi entries, I always added a method (class or object) that would initialize the known good defaults. Delphi also allows you to define constructor entries with parameters, but you cannot define your own constructor without parameters.

TSomeRecord = record Value1: double; Value2: double; procedure Init; end; procedure TSomeRecord.Init; begin Value1 := MaxDouble; Value2 := Pi; end; 

Given the above record, there is no warning that the record was not initialized. Developers can ignore the Init call in the record. Is there a way to automatically initialize default entries, potentially more than just FillChar ;

for example

 var LSomeRecord: TSomeRecord begin // someone forgot to call LSomeRecord.Init here FunctionThatTakesDefaultSomeRecord(LSomeRecord); end; 

How to automatically record to my default settings?

[Note] I do not want to change the problem after the answer. Any readers should read comments on best practices for using class methods to initialize instead of mutating object methods.

+10
delphi
source share
3 answers

You can use a hidden string field (which is automatically initialized with an empty string) to implement time-based initialization and implicit operators to hide implementation details. The code below shows how to implement a β€œdouble” field that is automatically initialized to Pi.

 program Project44; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils; type TAutoDouble = record private FValue: double; FInitialized: string; procedure Initialize(const val: double = Pi); public class operator Implicit(const rec: TAutoDouble): double; class operator Implicit(const val: double): TAutoDouble; end; TSomeRecord = record Value1: TAutoDouble; Value2: TAutoDouble; end; { TAutoDouble } procedure TAutoDouble.Initialize(const val: double); begin if FInitialized = '' then begin FInitialized := '1'; FValue := val; end; end; class operator TAutoDouble.Implicit(const rec: TAutoDouble): double; begin rec.Initialize; Result := rec.FValue; end; class operator TAutoDouble.Implicit(const val: double): TAutoDouble; begin Result.Initialize(val); end; var sr, sr1: TSomeRecord; begin try Writeln(double(sr.Value1)); Writeln(double(sr.Value2)); sr.Value1 := 42; Writeln(double(sr.Value1)); sr1 := sr; Writeln(double(sr.Value1)); Writeln(double(sr.Value2)); Readln; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end. 

However, there is no good way to make this solution more universal with respect to the default value - if you need a different default value, you need to clone the definition / implementation of TAutoDouble and change the default value.

+4
source share

AFAIK, you cannot but resort to tricks that are not worth it (perhaps using interface fields that are guaranteed to be initialized).

+2
source share

That would be a nice feature ... but I think you can use some kind of factory? or just a modest method returning a record ...

0
source share

All Articles