如何在项目之间共享记录结构,略有不同?

How to share records structure between projects, with slight variations?

我有 2 个项目处理相同的数据、不同的流程。主数组几乎相同,略有不同,例如:

// Project 1

TData1 = record
  A:string;
  B:integer;
  C:word;
  ...
end;

// Project 2

TData1 = record
  A:string;
  B:integer;
  C:word;
  ...
  XMLNode:TXMLNode; // Extra value needed in Project 2, not needed in Project 1
end;

我有很多数组想在项目之间共享。我想保留相同的数组结构,以便我可以复制并粘贴需要在两个项目中实现的任何未来更改。 有什么方法可以让记录保持相同但略有不同吗?

我在想这样的事情:

    // in Project 1:

    TExtras = record
    end;

    // in Project 2:

    TExtras = record
      XMLNode:TXMLNode;
    end;

    // shared - in both projects

    TData1 = record
      A:string;
      B:integer;
      C:word;
      ...
      Extras:TExtras; // different extra fields based on project needs
    end;

而且我可以在 Project2 的 Extras 中添加额外的字段,使用 Data1.Extras.XMLNode 访问字段。不确定这是否是面向未来的实施。

目标是有一天将所有共享结构放入共享单元,一个维护点,不再需要复制和粘贴。我需要数组来保持简单数组(array of TData1TArray<TData1>)的灵活性,所以我不能进入复杂的实现,这将限制选项轻松复制、排序、区分、操作数据......

这是正确的做法吗?还有更好的主意吗?


编辑:

两个项目都处理相同的数据,因此它们都从相同的 'source' 文件读取,但产生不同的最终结果。现在我有很多数组,其中 99% 在两个项目中用于相同的目的,相同的功能。 但是现在,当我在一个或另一个项目上工作时,添加新的记录字段、使用新字段的新函数,如果我不立即同步结构和新函数,那么几周后我将需要在项目 2 中做同样的事情,我将创建具有不同名称和不同函数名称的新字段。所以,当我最终在项目之间复制一些复杂的函数时,我发现它们不匹配只是因为命名不同。


编辑 2:

根据所有评论和建议,我决定走另一条路:在共享单元中共享通用数据结构和代码,并在项目 2 中创建具有额外记录字段的额外数组。我将创建这些新数组 link 到主数据数组,有:

// shared data 
TData1 = record
  A:string;
  B:integer;
  C:word;
  ...
end;

Data1:TArray<TData1>;

// additional in Project 2
TDataExtra = record
  DataIdx:integer;// link to TData1
  XMLNode:TXMLNode;
  ...
end; 

DataExtras:TArray<TDataExtra>;

可以简单地访问每个 Data1 记录的 XMLNode 值:

fGetXMLNode(i); // where i is index in Data1 array and function will return XMLNode

我相信有了这个我可以保留共享单元并向任何数组添加任何额外内容,只需最少的额外工作,这仍然比维护 2 个数据结构和代码的成本更低。

那会是更好的解决方案吗?

如果没有完整的上下文,很难说 best/better 哪种解决方案,但这里有一些其他方法可以实现相同的效果。

条件定义

TData1 = record
  A:string;
  B:integer;
  C:word;
  [...]
{$IFDEF NEEDXMLNODEINTDATA1}
  XMLNode:TXMLNode; // different extra fields based on project needs
{$ENDIF}
end;

使用新结构

在您的第二个应用程序中使用不同的结构。对于大多数情况,我希望这是正确的方法。

TData1Node = record
  Data1 : TData1;
  XMLNode : TXMLNode;
end;

始终包含 XMLNode

因为它只是一个指针,除非您的应用程序中有大量记录,否则它几乎不会记录内存使用情况。

Pascal 没有 void/Unit 数据类型,您仍然可以模拟它。 编译器并不总是对此感觉良好,在某些路径中它只是不 assumption/check 而在某些极端情况下 SizeOf(someType) 可能为零。

不过,如果您确实需要压缩项目 2 中的每个最后一个字节,同时保持与项目 1 共享源,则可以使用这个丑陋的 hack:

文件Project_1.inc

{$IFnDEF THIS_IS_PROJECT_1}
halt compilation! wrong setup!!!
{$EndIf}

{$IFDEF THIS_IS_PROJECT_2}
halt compilation! wrong setup!!!
{$EndIf}

type TDataPayload = TXMLNode;

文件Project_2.inc

{$IFnDEF THIS_IS_PROJECT_2}
halt compilation! wrong setup!!!
{$EndIf}

{$IFDEF THIS_IS_PROJECT_1}
halt compilation! wrong setup!!!
{$EndIf}

type REmpty = packed record end;

type TDataPayload = REmpty;

// after compilation - call `assert(SizeOf(TDataPayload) = 0);`

文件CommonDataType.pas

{$IfDef THIS_IS_PROJECT_1}
  {$INCLUDE Project_1.inc}
{$EndIf}

{$IfDef THIS_IS_PROJECT_2}
  {$INCLUDE Project_2.inc}
{$EndIf}

TData1 = 
{$IFDEF THIS_IS_PROJECT_2}
  packed
{$EndIf}
  record
    A:string;
    B:integer;
    C:word;
  ...
    Payload: TDataPayLoad;
  end;

话虽这么说,但我认为您不能通过项目 2 中的 type TXMLNode=byte 兼容性存根浪费单个字节的评估似乎很可疑。因为:

  1. 你用的是快memory-paddedrecord而不是慢bytes-squeezingpacked record
  2. 你使用 TXMLNode,即使在不同的项目中 - 每个 TObject 实例无论如何都会占用几十个字节,指向 RTTI 的指针,指向 VMT 的指针,堆管理器中的支持框架......记录将额外占用 4 或 8 个字节。而且你不能只支付一个额外的字节而不是所有的字节???快点!
  3. 您的 A:string; 在每条记录中为每个指针花费 4(或 8)个字节,然后 StringRec header 多花费 12 个字节,然后未知 pre-allocated just-in-case 字符串内容后的缓冲区,然后是尾随#0 的两个额外字节,然后是堆管理器中的支持数据结构。 type TXMLNode=boolean 存根的一个额外字节仍然太多了???
  4. 如果您的应用程序 performance/correctness 仅依赖于每个数据帧的一个额外字节,这也意味着由于用户将生成比平常多 5% 的数据,您的应用程序也会崩溃。时期。如果相信您的评估,那么您的应用程序已经达到标准,已经超过 algorithms/design 限制。并且应该改造为使用 memory-unbound 算法。一个简单的示例 - 您可以使用 on-disk 流式排序而不是使用 in-memory 快速排序,它具有相同的 worst-case 复杂度,但只需要 4 个数据帧的内存。

简单的解决方案是保持数据结构和相关函数在两个项目之间共享,并添加包含额外数据的额外数组,具体到每个项目。 使用额外的数组会带来更多的工作,但会保持共享代码的真正共享。

因此,而不是其他答案中提出的新 类 或 IFDefs,link 到主要数据的简单额外数组是解决问题的最佳选择:

// shared main data through both projects
TData1 = record
  A:string;
  B:integer;
  C:word;
  ...
end;

Data1:TArray<TData1>;

// additional in Project 2
TDataExtra = record
  DataIdx:integer;// link to TData1
  XMLNode:TXMLNode;
  ...
end; 

DataExtras:TArray<TDataExtra>;

使用此解决方案,任何其他数组(如 DataExtras 或其他数组的任何其他额外字段)都可以轻松添加和扩展,而无需更改共享代码。共享代码将易于维护,因为它只包含主要数据,没有任何特定于一个项目的东西