记录的通用助手

Generic helper for record

目前我有很多这样的辅助方法的记录:

TRec1 = packed record
  S1: string;
  S2: string;
  procedure FromJSON(const AJSON: string);
  function  ToJSON: string;
end;

procedure TRec1.FromJSON(const AJSON: string);
begin
  RecordLoadJSON(Self, StringToUTF8(AJSON), TypeInfo(TRec1));
end;

function TRec1.ToJSON: string;
begin
  Result := UTF8ToString(RecordSaveJSON(Self, TypeInfo(TRec1)));
end;

TRec2 = packed record
  S1: string;
  I1: string;
  procedure FromJSON(const AJSON: string);
  function  ToJSON: string;
end;

procedure TRec2.FromJSON(const AJSON: string);
begin
  RecordLoadJSON(Self, StringToUTF8(AJSON), TypeInfo(TRec2));
end;

function TRec2.ToJSON: string;
begin
  Result := UTF8ToString(RecordSaveJSON(Self, TypeInfo(TRec2)));
end;

如您所见,所有记录都包含相同的 ToJSON 和 FromJSON 方法。 此方法包含完全相同的代码,除了 TypeInfo()

有什么方法可以为此使用泛型并且不为每个记录声明此方法?

记录或classes?
尽管 Delphi 支持方法,但记录仍然是第二 class 公民,因为不支持记录继承。
类 拥有记录的所有功能以及全套 OOP 功能,因此它们通常是更好的选择。但是,您需要处理需要在非 ARC 平台上手动管理 classes 的创建和销毁的引用语义,以及具有自动清理功能的记录的无忧值语义。

非泛型解决方案(使用 class)
将字符串容器声明为 class 并使用继承。

TRec1 = class(TPersistent)
  S1: string;
  S2: string;
  procedure FromJSON(const AJSON: string);
  function  ToJSON: string;
end;

TRec2 = class(TRec1)
end;

procedure TRec1.FromJSON(const AJSON: string);
begin
  RecordLoadJSON(Self, StringToUTF8(AJSON), Self.ClassInfo);
end;

function TRec1.ToJSON: string;
begin
  Result := UTF8ToString(RecordSaveJSON(Self, Self.ClassInfo));
end;

所有从 TPersistent 继承的 classes 都有 RTTI,因此 self.classinfo 有效。 您可能需要根据需要修改 RecordSaveJSON 调用。

泛型解决方案
如果您为您的单元 {$M+} 启用了 RTTI 并且您必须使用记录,只需使用记录之外的方法并将记录类型作为通用参数提供。

TRec1 = record
  data: typex;
  ....
end;

TDoJSONThings = record     //helper record for any and all types with RTTI.
  procedure FromJSON<T:record>(var Data: T; const JSON: string); static;
  function ToJSON<T:record>(const [ref] Data: T): string; static;
end;

procedure TDOJSONThings.FromJSON<T>(var Data: T; const JSON: string); static;
var
  pt: PTypeInfo;
begin
  pt:= TypeInfo(T);
  RecordLoadJSON(Data, StringToUTF8(AJSON), pt);
end;

function TDOJSONThings.ToJSON<T:record>(const [ref] Data: T): string; static;
var
  pt: PTypeInfo;
begin
  pt:= TypeInfo(T);
  Result:= UTF8ToString(RecordSaveJSON(Data, pt));
end;

备注
使用 record helper(如 THelper = record helper for TRec1)将不起作用,因为这仅适用于 TRec1 并且很遗憾,记录不支持继承。

最好使用独立方法,但 Delphi 不允许不属于记录或 class 一部分的泛型方法。

哦,请放弃 1970 年代的 packed record 风格构造。它没有任何作用,只是通过错位让你的代码变慢。