问题
To initialize Delphi records I've always added a method (class or object) that would initialize to known good defaults. Delphi also allows for defining record "constructors" with parameters, but you cannot define your own parameter-less "constructor".
TSomeRecord = record
Value1: double;
Value2: double;
procedure Init;
end;
procedure TSomeRecord.Init;
begin
Value1 := MaxDouble;
Value2 := Pi;
end;
Given the record above there is no warning that a record has not been initialized. Developers may neglect to call Init on the record. Is there a way to automatically initialize records to my default, potentially more than just a simple FillChar;
For instance
var
LSomeRecord: TSomeRecord
begin
// someone forgot to call LSomeRecord.Init here
FunctionThatTakesDefaultSomeRecord(LSomeRecord);
end;
How can a record be initialized to my defaults automatically?
[Note] I don't want to modify the problem after it has been answered. Any readers are directed to read the comments on best practices on using class methods for initialization instead of a mutating object method.
回答1:
You can use a hidden string field (which is automatically initialized to an empty string) to implement 'on time' initialization and implicit operators to hide implementation details. The code below shows how to implement a 'double' field which 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.
There's, however, no nice way to make this solution more generic regarding the default value -- if you need a different default value you have to clone TAutoDouble definition/implementation and change the default value.
回答2:
AFAIK you can't without resorting to tricks that aren't worth it (maybe using interface fields which are guaranteed to be initialized).
回答3:
This would be a nice feature ... but I guess you can use a factory of some kind? or just a humble method returning a record...
来源:https://stackoverflow.com/questions/39392920/how-can-delphi-records-be-initialized-automatically