Delphi:更好的设计以避免循环单元引用?

2024-02-26

我在 Delphi 10 中有一个三角形网格结构。

出于性能原因,我将网格顶点、三角形面等的数据存储在 TList 的后代中。

我让 TList 为列表中的每个成员进行计算。对于这些计算,我需要访问 TMesh 结构的某些字段。因此,在创建 TMesh 以及随后创建列表的过程中,我将父 TMesh 分配给列表。我使用 TMesh 的前向声明来执行此操作。请看下面的代码:

type
  {forward declaration}
  TMesh=class;

  TVertex=record
    Point: TPoint3D;
    //other fields
  end;

  TVertices=class(TList<TVertex>)
    Mesh: TMesh;
    procedure DoSomethingWithAllVertices; //uses some fields of TMesh
    constructor Create(const AMesh: TMesh);
    //other methods
  end;

  TTriangleFace=record
    Vertices: Array[0..2] of Integer;
    //other fields
  end;

  TTriangleFaces=class(TList<TTriangleFace>)
    Mesh: TMesh;
    procedure DoSomethingWithAllTriangleFaces; //uses some fields of TMesh
    constructor Create(const AMesh: TMesh);
    //other methods
  end;

  TMesh=class(TComponent)
    Vertices: TVertices;
    TriangleFaces: TTriangleFaces;
    constructor Create(AOwner: TComponent);
    //other fields & methods
  end;

implementation

constructor TMesh.Create(AOwner: TComponent);
begin
  inherited;
  Vertices:=TVertices.Create(Self);
  TriangleFaces:=TTriangleFaces.Create(Self);
end;

constructor TVertices.Create(const AMesh: TMesh);
begin
  Mesh:=AMesh;
end;

这很好用。

然而,由于我的项目不断增长,我得到的代码越来越多,我想将列表类分布在单独的单元中。这导致了循环单元引用的问题。

循环单元引用的问题似乎是众所周知的。我检查了可能的解决方案,但找不到适合我的问题的解决方案。有人说,如果遇到循环单元引用,则代码设计得很糟糕。

如何改进设计并同时保持较高的计算性能?

解决问题的其他可能性是什么?

非常感谢!


前向声明不能跨单元工作。当一个单元转发声明一个记录/类时,同一单元也必须定义该记录/类。

我建议定义一个IMesh接口TMesh实现,然后有TVertices and TTriangleFaces use IMesh代替TMesh直接地。这样,就不存在循环引用,并且接口可以公开所需的任何字段值的属性。和TComponent禁用已实现接口的引用计数,因此内存泄漏不是问题。

MeshIntf.pas:

unit MeshIntf;

interface

type
  IMesh = interface(IInterface)
    ['{30315BC6-9A2E-4430-96BB-297D11C9DB5D}']
    // methods for performing common tasks...
    // properties for reading/setting needed values...
  end;

implementation

end.

顶点.pas:

unit Vertices;

interface

uses
  System.Types, System.Generics.Collections, MeshIntf;

type
  TVertex = record
    Point: TPoint3D;
    //other fields
  end;

  TVertices = class(TList<TVertex>)
  public
    Mesh: IMesh;
    constructor Create(const AMesh: IMesh); reintroduce;
    procedure DoSomethingWithAllVertices;
    //other methods
  end;

implementation

constructor TVertices.Create(const AMesh: IMesh);
begin
  inherited Create;
  Mesh := AMesh;
end;

procedure TVertices.DoSomethingWithAllVertices;
begin
  // use properties/methods of Mesh as needed...
end;

end.

TriangleFaces.pas:

unit TriangleFaces;

interface

uses
  System.Generics.Collections, MeshIntf;

type
  TTriangleFace = record
    Vertices: Array[0..2] of Integer;
    //other fields
  end;

  TTriangleFaces = class(TList<TTriangleFace>)
  public
    Mesh: IMesh;
    constructor Create(const AMesh: IMesh); reintroduce;
    procedure DoSomethingWithAllTriangleFaces;
    //other methods
  end;

implementation

constructor TTriangleFaces.Create(const AMesh: IMesh);
begin
  inherited Create;
  Mesh := AMesh;
end;

procedure TTriangleFaces.DoSomethingWithAllTriangleFaces;
begin
  // use properties/methods of Mesh as needed...
end;

end.

Mesh.pas:

unit Mesh;

interface

uses
  Classes, MeshIntf, Vertices, TriangleFaces;

type
  TMesh = class(TComponent, IMesh)
  public
    Vertices: TVertices;
    TriangleFaces: TTriangleFaces;
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    //other fields & methods, and IMesh implementation
  end;

implementation

constructor TMesh.Create(AOwner: TComponent);
begin
  inherited;
  Vertices := TVertices.Create(Self as IMesh);
  TriangleFaces := TTriangleFaces.Create(Self as IMesh);
end;

destructor TMesh.Destroy;
begin
  Vertices.Free;
  TriangleFaces.Free;
  inherited;
end;

end.

如果你不需要TMesh为了在设计时可供表单设计器和对象检查器使用,您应该从TInterfacedObject代替TComponent。但随后您需要进行一些小调整才能正确处理引用计数(其中TComponent禁用)。尤其,TVertices and TTriangleFaces需要使用弱引用以免增加TMesh的引用计数并导致内存泄漏(因为在这种情况下其引用计数将永远降至 0):

MeshIntf.pas:

unit MeshIntf;

interface

uses
  System.Types;

type
  TVertex = record
    Point: TPoint3D;
    //other fields
  end;

  IVertices = interface(IInterface)
    ['{97A70A11-C8B6-4DBC-807B-B9E0C6953B9E}']
    // methods for performing tasks...
    procedure DoSomethingWithAllVertices;
    function GetVertex(Index: Integer): TVertex;
    // properties for reading/setting values...
    property Vertex[Index: Integer]: TVertex read GetVertex;
  end;

  TTriangleFace = record
    Vertices: Array[0..2] of Integer;
    //other fields
  end;

  ITriangleFaces = interface(IInterface)
    ['{A1ED479B-7430-4524-A630-FDDE212375BB}']
    // methods for performing tasks...
    procedure DoSomethingWithAllTriangleFaces;
    function GetFace(Index: Integer): TTriangleFace;
    // properties for reading/setting values...
    property Face[Index: Integer]: TTriangleFace read GetFace;
  end;

  IMesh = interface(IInterface)
    ['{30315BC6-9A2E-4430-96BB-297D11C9DB5D}']
    // methods for performing common tasks...
    function GetVertices: IVertices;
    function GetTriangleFaces: ITriangleFaces;
    // properties for reading/setting values...
    property Vertices: IVertices read GetVertices;
    property TriangleFaces: ITriangleFaces read GetTriangleFaces;
  end;

implementation

end.

顶点.pas:

unit Vertices;

interface

uses
  System.Generics.Collections, MeshIntf;

type
  TVertices = class(TInterfacedObject, IVertices)
  private
    // Delphi 10.1 Berlin adds [weak] support to all compilers,
    // it was previously only available on the mobile compilers...
    {$IFDEF WEAKINTFREF}
    [weak] fMesh: IMesh;
    {$ELSE}
    fMesh: Pointer;
    {$ENDIF}

    fVertices: TList<TVertex>;

  public
    constructor Create(AMesh: IMesh);
    destructor Destroy; override;

    //other methods

    // IVertices implementation
    procedure DoSomethingWithAllVertices;
    function GetVertex(Index: Integer): TVertex;
  end;

implementation

constructor TVertices.Create(AMesh: IMesh);
begin
  inherited Create;
  fMesh := {$IFDEF WEAKINTFREF}AMesh{$ELSE}Pointer(AMesh){$ENDIF};
  fVertices := TList<TVertex>.Create;
end;

destructor TVertices.Destroy;
begin
  fVertices.Free;
  inherited;
end;

procedure TVertices.DoSomethingWithAllVertices;
begin
  // use properties of fMesh as needed...

  // if WEAKINTFREF is not defined simply type-cast the Mesh
  // pointer as IMesh(fMesh) when accessing its members...
end;

function TVertices.GetVertex(Index: Integer): TVertex;
begin
  Result := fVertices[Index];
end;

end.

TriangleFaces.pas:

unit TriangleFaces;

interface

uses
  System.Generics.Collections, MeshIntf;

type
  TTriangleFaces = class(TInterfacedObject, ITriangleFaces)
  private
    // Delphi 10.1 Berlin adds [weak] support to all compilers,
    // it was previously only available on the mobile compilers...
    {$IFDEF WEAKINTFREF}
    [weak] fMesh: IMesh;
    {$ELSE}
    fMesh: Pointer;
    {$ENDIF}

    fFaces: TList<TTriangleFace>;

  public
    constructor Create(AMesh: IMesh);
    destructor Destroy; override;

    //other methods

    // ITriangleFaces implementation
    procedure DoSomethingWithAllTriangleFaces;
    function GetFace(Index: Integer): TTriangleFace;
  end;

implementation

constructor TTriangleFaces.Create(AMesh: IMesh);
begin
  inherited Create;
  fMesh := {$IFDEF WEAKINTFREF}AMesh{$ELSE}Pointer(AMesh){$ENDIF};
  fFaces := TList<TTriangleFace>.Create;
end;

destructor TTriangleFaces.Destroy;
begin
  fFaces.Free;
  inherited;
end;

procedure TTriangleFaces.DoSomethingWithAllTriangleFaces;
begin
  // use properties of fMesh as needed...

  // if WEAKINTFREF is not defined simply type-cast the Mesh
  // pointer as IMesh(fMesh) when accessing its members...
end;

function TTriangleFaces.GetFace(Index: Integer): TTriangleFace;
begin
  Result := fFaces[Index];
end;

end.

Mesh.pas:

unit Mesh;

interface

uses
  MeshIntf;

type
  TMesh = class(TInterfacedObject, IMesh)
  private
    // note, these are *strong* references, not*weak* references!
    fVertices: IVertices;
    fTriangleFaces: ITriangleFaces;

  public
    constructor Create;

    //other fields & methods

    // IMesh implementation
    function GetVertices: IVertices;
    function GetTriangleFaces: ITriangleFaces;
  end;

implementation

uses
  Vertices, TriangleFaces;

constructor TMesh.Create;
begin
  inherited;
  fVertices := TVertices.Create(Self as IMesh);
  fTriangleFaces := TTriangleFaces.Create(Self as IMesh);
end;

function TMesh.GetVertices: IVertices;
begin
  Result := fVertices;
end;

function TMesh.GetTriangleFaces: ITriangleFaces;
begin
  Result := fTriangleFaces;
end;

end.

只要确保你有一个不弱的IMesh创建时在代码中的某处变量TMesh对象,以便它保持活动状态,直到您不再需要它为止:

var
  Meth: IMesh; // or a class member or a global, wherever you need it

Mesh := TMesh.Create;
...
Mesh := nil;

(正确的)引用计数将为您处理剩下的事情。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Delphi:更好的设计以避免循环单元引用? 的相关文章

  • 从命令行增加 Delphi XE 项目版本号

    我有一个 Delphi XE 项目 我试图在使用 MSBuild 构建它之前更改程序的版本号 版本号信息位于 DPROJ 文件中 但如果我更改这些值 版本号不会更改 我认为原因是当您在 IDE 中更改版本号时 Delphi 会将更改保存到
  • 如何在以管理员身份运行模式下部署应用程序?

    如何部署应用程序 使其需要管理员权限 而无需最终用户手动执行此操作 我使用 Delphi 2009 来构建该应用程序 您可以使用以下命令通知 Windows 您的应用程序需要以管理员身份运行requestedExecutionLevel应用
  • 如何解决内存分段并强制FastMM释放内存给OS?

    注意 32 位应用程序不计划迁移到 64 位 我正在使用一个非常消耗内存的应用程序 并且几乎优化了与内存分配 取消分配相关的所有相关路径 应用程序本身没有内存泄漏 没有句柄泄漏 没有任何其他类型的泄漏 据我所知并经过测试 我无法触及的第 3
  • XE2 中的 COM 是否损坏?我该如何解决它?

    Update XE2 Update 2 修复了下述错误 下面的程序是从实际程序中截取的 在 XE2 中失败并出现异常 这是 2010 年的回归 我没有 XE 来测试 但我希望该程序在 XE 上运行良好 感谢 Primo 确认代码在 XE 上
  • Delphi 如何与 Active Directory 集成?

    我们需要使用 Delphi 7 验证 Microsoft Active Directory 上的用户 最好的方法是什么 我们可以有两种情况 用户输入其网络用户名和密码 其中用户名可能包括域 然后我们检查活动目录是否是有效的活动用户 或者我们
  • Delphi - 通过 ADO 查询获取 Excel 行

    我有以下 Excel 文件 我将 AdoConnection ConnectionString 设置为 AdoConnection ConnectionString Provider Microsoft Jet OLEDB 4 0 Data
  • ComboBox.Sorted 发生了什么:= True;在德尔福 10.2 中?

    最近我的最后一个问题获得了 风滚草 徽章 我不确定是否应该问更多问题 但这里是 我正在填充一个TComboBox使用 sqlite 表中的项目 效果很好 在我以前的 Delphi 版本中 我能够使用ComboBox1 Sorted True
  • 递归遍历树视图中的节点?

    我有一个树视图 其中已经填充了另一个过程中的文件 文件夹 我想按照从上到下的确切顺序逐项迭代树视图中的项目 但是 与普通列表不同 我不能仅使用简单的for对此的声明 我必须进入每个节点等 我该怎么做呢 我希望有一种方法可以在不运行递归过程的
  • 在运行时创建 TQReport 元素

    在运行时创建 TQReport 元素 嗯 至少尝试一下 我不知道这份报告中应出现哪些标题或数据 我得到一个代表数据行和列的 TString 的 TList 我在组的带打印事件中植入 创建 指令 并在主数据行带的 OnNeedData 事件中
  • 如何将 ImmutableJS Map 与 TypeScript 结合使用

    我有一个看起来像这样的树结构 interface TreeData id number text string children TreeData 我想将其包装到一个不可变的映射中 但由于我使用的是 TypeScript 所以我希望使用 g
  • 如何避免使用 WinApi.Windows 的 Delphi 应用程序中的 dll 劫持

    Delphi 最新版本使用各种系统 dll 的静态链接 例如 WinApi Windows 单元中的 version dll 这会导致在单元初始化之前加载 version dll 这会打开一个安全漏洞 可以通过将受感染的 version d
  • 如何在滚动框上创建缓慢的滚动效果?

    我喜欢在滚动框中平移图像后创建平滑的减慢滚动效果 就像平移地图一样谷歌地图 http maps google com 我不确定它是什么类型 但行为完全相同 当快速移动地图时 当您释放鼠标时它不会立即停止 而是开始减慢速度 有什么想法 组件
  • Firemonkey 编辑/组合自动完成/打字时自动建议

    实施方式是什么Autocomplete or Autosuggest适用于 Windows Android 平台以及 MacOS 和 iOS 的 Delphi Firemonkey Example 当用户在 Google 搜索框中输入文本时
  • 如何仅使用 TADOQuery 组件将图像插入数据库

    我有一个简单的基本问题 我正在尝试使用将图像插入数据库Insert与其他列值的语句也使用TADOQuery成分 由于代码已经由某人编写 因此我想在此处放置一些虚拟示例代码 以供您澄清相应的步骤 请注意 这可以正常工作TQuery组件 因为我
  • 如何遍历任意给定集合中的枚举?

    我有很多枚举类型 它们与相应的集合相结合 例如 type TMyEnum meOne meTwo meThree TMyEnums set of TMyEnum 我正在尝试提出一组可以运行的函数any枚举集 而不是为每个枚举编写单独的函数
  • 有人用CrossKylix进行真正的跨平台开发吗?

    新版本克罗斯凯利克斯 http crosskylix untergrund net 两周前更新过 即使 Kylix 已经停产很久了 但它似乎仍然被一些 Delphi 开发人员使用 有人在 Windows 和 Linux 的跨平台开发中成功使
  • 如何在Delphi中下载一个非常简单的HTTPS页面?

    我尝试了在这里看到的代码 但它不适用于 HTTPS 我需要将此页面作为字符串下载 并在其上添加一些换行符 以便将信息按顺序放入 TMemo 中 怎么做 我尝试使用 Indy 但由于 SSL 问题而失败 我尝试了此页面的解决方案 如何将网页下
  • 如何在 Delphi DBLookupComboBox 中选择正确的项目

    我有一个数据库查找组合框连接到数据库查询 那部分工作正常 当我运行程序时数据库查找组合框填充有查询的结果 我想看看数据库查找组合框填充第一项 请选择 当 的时候程序第一次运行或者当一个新项目行动已启动 见下图 另外 如果我正在加载以前保存的
  • 以编程方式重新启动 Delphi 应用程序

    应该不可能运行我的应用程序的多个实例 因此项目源码包含 CreateMutex nil False PChar ID if GetLastError ERROR ALREADY EXISTS then Halt 现在我想以编程方式重新启动我
  • Delphi 2009 IDE结构视图折叠功能

    现在有谁知道折叠 Delphi 2009 IDE 结构视图中的所有项目吗 我不知道折叠所有项目 这使我很难从视图中找到我的课程 Thanks 选择根节点 类 并按数字键盘上的 除号 那会让一切崩溃 然后按数字键盘上的 加号 键 第一级将展开

随机推荐

  • React Native:用选项卡动画缩小标题

    Goal 我试图创建一个带有动画收缩标题的视图 其中包含带有滚动内容的选项卡的选项卡视图 参见图片 Setup 我正在使用带有 TabNavigator 的反应导航 header 是一个具有固定高度的组件 当前位于 TabNavigator
  • 使用 googletest 测试受保护成员

    谷歌测试时我对继承感到困惑 我有一个class A具有protected属性 如果我想访问那些我必须扩展该类 但同时我也需要扩展public testing Test唯一的目的是gtest 这个问题最优雅的解决方案是什么 我也在努力避免 d
  • 错误:1210:执行准备好的语句的参数数量不正确

    我正在尝试使用 Python 将数据插入 MySQL 出现这个错误的原因是什么 编程错误 1210 执行的参数数量不正确 准备好的声明 我的Python代码 connection mysql connector connect host l
  • UISearchController 搜索栏在第一次单击时消失

    我在 TableView 中实现了 UISearchController 由导航控制器推动 首先我的问题是 每当我单击搜索栏时 它就会消失 当我输入一些文本时它起作用 但它保持完全空白 然后我设法使用以下代码半解决了该问题 void sea
  • 对 ASP.NET Core 中缺少必需属性的响应

    给定以下控制器 using System ComponentModel DataAnnotations using Microsoft AspNetCore Mvc namespace WebApplication1 Controllers
  • Scala 中未绑定的可比较排序

    我对 Scala 中的排序有点熟悉Ordering的 但是我想对 Java 中定义的一些对象进行排序 他们是Comparable not Comparable T and final final class Term implements
  • Slug 大小对于 Heroku 上的 Flask 应用程序来说太大

    我正在部署一个非常简单的烧瓶应用程序 带有面部识别模型 我只是将 Flask 应用程序代码和模型权重推送到 Heroku 我的 slug 大小仍然是 556M 超过了 500M 的限制 我在requirements txt 中有最低要求 这
  • 为什么从返回 int32_t 的函数返回 0x80000000 不会导致警告?

    考虑 int32 t f return 0x80000000 为什么这不会导致编译器警告 至少在 GCC 上 0x80000000 超出范围int32 t INT32 MAX is 0x7fffffff 我相信这应该会导致隐式转换 这是正确
  • 在 AWS Lambda 函数中使用 Django ORM

    我有一个现有的 Django 应用程序数据存储在 Postgres RDS 下 现在我想通过 lambda AWS 函数和 Django 风格的 ORM 查询 更新数据 我知道理论上这是可能的 如果 使用 lambda 函数打包整个 Dja
  • 如何进行水平视差滚动

    我正在使用最新版本的 Bootstrap JQuery 和 Skrollr 我想要一个静态背景和几个在您通过视差滚动滚动时出现的场景 我可以在您滚动时制作场景 但我正在寻找一种方法 让您看起来不会向下移动页面 我正在寻找像这样的图像的场景
  • 使用 BigQuery 查询地理空间数据

    您好 我想获取基于 GPS 坐标的公共场所 餐厅 酒店 电影院等 邻居列表 BigQuery 可以做到这一点吗 如果您将经纬度或 GPS 坐标作为列 那么您绝对可以使用坐标上的 WHERE 比较从 BigQuery 中获取矩形区域 然后在选
  • 在 Windows 10 中找不到 tools.jar React Native Android

    伙计们 我只是尝试在我的笔记本电脑上安装 React Native 我已遵循所有设置说明 但仍然收到这些错误 What went wrong Execution failed for task app compileDebugJavaWit
  • 使用训练有素的 Spark ML 模型提供实时预测[重复]

    这个问题在这里已经有答案了 我们目前正在测试一个基于 Spark 在 Python 中实现 LDA 的预测引擎 https spark apache org docs 2 2 0 ml clustering html latent diri
  • 如何使用堆在线性时间内找到数字的中位数?

    维基百科 http en wikipedia org wiki Heap data structure Heap applications says 选择算法 找到最小值 最大值 最小值和最大值 median 或者 甚至第 k 大元素也可以
  • 在Windows中设置子进程名称?

    我有一个进程 它运行多次子进程 每个子进程都没有 GUI 并且需要为任务管理器的所有子进程设置不同的 名称 和 描述 可以使用Win API吗 我找不到 Windows 系列的解决方案 我查看了 WriteProcessMemory 但它看
  • 使用 JavaScript 的 Selenium Webdriver,如何使用 chrome.exe 的特定路径启动 Chrome?

    我有以下 Javascript 代码 它使用由指定的 Chrome 路径启动 ChromePATH环境变量 let driver await new Builder forBrowser chrome build 如何使用 Chrome 的
  • 在 Python 中打开 URL 并获取最多 X 字节的最佳方法是什么?

    我想让机器人每小时获取一个 URL 但如果网站运营商是恶意的 他可以让他的服务器向我发送一个 1 GB 的文件 有没有好的方法可以将下载限制为 100 KB 并在该限制之后停止 我可以想象从头开始编写自己的连接处理程序 但如果可能的话 我想
  • 设置从磁盘加载的背景图像

    我想在运行时更改 QFrame 背景 但是 我会从磁盘 图像 加载背景 在 QFrame 中设置样式表不起作用 因为图像不在资源中 一种方法是设置 QPushButton 图标 例如 QPixmap img images 01 png ui
  • JMeter:为单个用户触发多个并发 HTTP 请求

    我有一个带有线程组和 Cookie 管理器的 JMeter 负载测试脚本 线程组中的用户首先使用HTTP采样器登录来获取cookie 然后 循环控制器触发交错采样器 该采样器在几个向服务器发出查询的 HTTP 采样器之间交替 现在 我希望交
  • Delphi:更好的设计以避免循环单元引用?

    我在 Delphi 10 中有一个三角形网格结构 出于性能原因 我将网格顶点 三角形面等的数据存储在 TList 的后代中 我让 TList 为列表中的每个成员进行计算 对于这些计算 我需要访问 TMesh 结构的某些字段 因此 在创建 T