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:平滑折叠/展开形式

    需要你的帮助 我一直在寻找 我在德尔福西雅图 试图平滑调整表单底部的大小 就我而言 调整大小 只是有点折叠 展开 如下所示 我怎样才能意识到这一点 我尝试过使用 TTimer procedure TForm1 Timer1Timer Sen
  • NvCplGetThermalSettings 返回 false

    问题 您好 我正在尝试使用 Delphi 获取 nividia gtx 980 的 GPU 温度 我看过C 问题 他的解决方案是不使用nvcpl dll 我认为这不是正确的解决方案 因为 nivida 有完整的文档说明如何处理 API 见下
  • 如何发送/接收 JSON?

    我知道这是一个愚蠢的问题 但我通常不进行 Web 编程 并且被要求发送一些 JSON 数据并接收一些回报 我用谷歌搜索 很清楚如何编码和解码 JSON 数据 但我对此很陌生 我不知道如何与给定的 URL 进行通信并说 这里有一些数据 请发回
  • 如何在 Delphi 中更改 TabControl 中活动 TAB 的颜色

    如何更改 TabControl 在 FireMonkey 上 中活动 TAB 的颜色 如下所示 有两种方法可以实现这一点 1 第一个选项是您可以创建定制风格 for 选项卡控件 from T样本 风格设计师 然后您可以添加您想要在自定义设计
  • 检测 TWebBrowser 文档中的活动元素何时发生变化

    是否有任何我可以挂钩的事件来检测网页上的活动元素何时发生变化 例如 当用户聚焦编辑框时 我知道我可以检查计时器中的活动元素 但如果可能的话我宁愿避免这种情况 这并不是对您的问题的完整答案 但希望能帮助您完成大部分工作 对于通过类似的 q 到
  • Delphi 2009之前如何处理UTF-8和ANSI转换?

    在 Delphi 2009 中 我们有 RichEdit1 Lines LoadFromFile OpenDialog1 FileName TEncoding UTF8 RichEdit1 Lines SaveToFile OpenDial
  • 将 TPopupMenu 与窗体的右侧对齐?

    TPopupMenu 如何与窗体的右侧对齐 问题是 在调用之前似乎没有办法获取弹出菜单的宽度Popup X Y Integer 我正在尝试获得与 Chrome 中的系统菜单类似的行为 你也可以只设置Alignment http docwik
  • 在 Pascal 中将文本文件中的字符串读入数组

    使用这个程序 我试图读取一个文件并将其随机打印到控制台 我想知道是否必须使用数组 例如 我可以将字符串分配到一个数组中 然后从数组中随机打印 但是 我不确定如何解决这个问题 另一个问题是 我当前的程序没有从我的文件中读取第一行 我有一个文本
  • 可以在delphi数据集中创建一个假数据字段吗?

    我想在 DataSet 不是 ClientDataSet 中创建一个 假 数据字段 该字段不应存储在数据库中 它不是计算字段 应允许用户输入输入数据 该字段具有业务逻辑含义 因此用户更新其值后应该更新其他字段 使用 OnFieldChang
  • Firemonkey - 更新视觉组件

    我们从版本 1 开始就使用 Firemonkey 但仍然发现更新当前在屏幕上可见的组件很困难 在 Firemonkey 中请求重画的 方式 有很多 也许太多了 应用样式 ApplyStyle 事件 主要是当它在屏幕上可见时 请求 repai
  • 在主表单之前显示登录表单

    我在表单之间导航时遇到问题 我使用 Delphi XE5 创建了一个 Android Firemonkey 移动应用程序 我目前有一个登录表单和主表单 现在我想要有关如何处理登录表单以显示在主表单之前的建议 在 项目选项 中的表单下 选择要
  • Winform 没有.NET 框架?

    我必须创建一些表单并将其作为直接 EXE 提供 而不是安装程序 它安装 NET 框架 最终用户对此不满意 他们想要可以直接打开和工作的东西 我知道它可以作为网络完成 但我正在寻找 winforms 吗 请建议哪种工具 技术可以处理这个问题
  • Delphi - 相当于C#的三元运算符? [复制]

    这个问题在这里已经有答案了 可能的重复 Delphi 中是否存在或者将来是否存在条件运算符 https stackoverflow com questions 2108609 is there or is there ever going
  • Delphi 的免费加密库 [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在为 Delphi 2010 寻找一个免费且最新的加密库 该库实现 RSA 和 AES Rijnda
  • 如何读取注册表项的默认值

    我有一个 Delphi XE2 项目来使用注册表项进行某些操作 所以我定义了以下代码 procedure TMainForm BitBtn01Click Sender TObject var RegistryEntry TRegistry
  • 如何追踪手柄泄漏?

    在我的一个应用程序中 我观察到句柄数量不断增加 在不使用应用程序的情况下 该数字大约每秒增加一次 因此后台处理代码的某些部分一定存在句柄泄漏 我如何追踪此类泄漏 有什么工具可以帮助解决这个问题吗 跟踪句柄泄漏时要寻找哪些模式 导致手柄泄漏的
  • Delphi XE5 REST/Android 客户端“会话已过期”

    我有一个REST Server与Android Client 都在Deplhi Xe5 Android客户端成功连接Rest服务器 在我的服务器中我有一个TDSHttpWebDispatcher with SessionTimeout 12
  • 如何追踪“地址 00000000”的访问违规

    我知道如何创建 map 文件来在错误消息包含实际地址时跟踪访问冲突错误 但是如果错误消息说怎么办 Access violation at address 00000000 Read of address 00000000 我从哪里开始寻找这
  • 在 Delphi 2007 中将具有透明度的位图保存为 PNG

    我有一个包含透明度信息的 Delphi 位图 32 位 我需要将其转换并保存为 PNG 文件 同时保留透明度 我目前拥有的工具是graphics32 Library GR32 PNG 由Christian Budde 提供 和PNGImag
  • 在TImageViewer中,如何获取用户点击图片的位置?

    在TImageViewer控件中 用户可以缩放或平移图片 我的问题是 当用户点击图片时 如何获取用户在图片上的点击位置 尤其是用户可以对图片进行放大 缩小或平移之后 如何获取对应的图片点击位置呢 As shown below How to

随机推荐

  • 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