如何自动初始化Delphi记录?

Jas*_*out 10 delphi

要初始化Delphi记录,我总是添加一个初始化为已知良好默认值的方法(类或对象).Delphi还允许使用参数定义记录"构造函数",但是您无法定义自己的无参数"构造函数".

TSomeRecord = record
 Value1: double;
 Value2: double;

 procedure Init;
end;

procedure TSomeRecord.Init;
begin
  Value1 := MaxDouble;
  Value2 := Pi;
end; 
Run Code Online (Sandbox Code Playgroud)

鉴于上述记录,没有警告说记录尚未初始化.开发人员可能会忽略调用Init记录.有没有办法自动将记录初始化为我的默认值,可能不仅仅是一个简单的FillChar;

例如

var 
  LSomeRecord: TSomeRecord
begin
  // someone forgot to call LSomeRecord.Init here
  FunctionThatTakesDefaultSomeRecord(LSomeRecord);
end; 
Run Code Online (Sandbox Code Playgroud)

如何将记录自动初始化为我的默认值?

[注意]我不想在答案后修改问题.任何读者都可以阅读关于使用类方法进行初始化而不是变异对象方法的最佳实践的注释.

小智 7

从Delphi 10.4开始,有自定义管理记录

  TMyRecord = record
    Value: Integer;
    class operator Initialize (out Dest: TMyRecord);
    class operator Finalize (var Dest: TMyRecord);
  end;
Run Code Online (Sandbox Code Playgroud)

这种结构与以前可用于记录的结构之间的巨大区别在于自动调用。


gab*_*abr 5

您可以使用隐藏字符串字段(自动初始化为空字符串)来实现“准时”初始化和隐式运算符来隐藏实现细节。下面的代码显示了如何实现一个自动初始化为 Pi 的“double”字段。

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.
Run Code Online (Sandbox Code Playgroud)

但是,没有好的方法可以使这个解决方案在默认值方面更通用——如果您需要不同的默认值,则必须克隆 TAutoDouble 定义/实现并更改默认值。

  • 这是其中之一,虽然你可以做到,但你不应该 (7认同)
  • 这里的技巧是向由编译器/运行时初始化的记录添加一个字段(它适用于任何托管类型,就像 IInterface 或字符串一样),这样你就有了一个初始化状态的标志。我提到的单元使用手工制作的接口方法表来避免为了获得接口引用而创建对象的开销(`System.Rtti.pas` 使用相同的技术 - 查看`TValue.Create`)。Allen Bauer 在他关于可空值的博客文章中使用了相同的方法(参见 http://community.embarcadero.com/blogs/entry/a-andquotnullableandquot-post-38869)。 (3认同)
  • 根据记录,我同意大卫的观点。 (2认同)