- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我在 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;
这很好用。
但是,由于我的项目不断增长,我得到的代码越来越多,我想将列表类分布在单独的单元中。这导致了循环单元引用的问题。
循环单元引用的问题似乎众所周知。我检查了可能的解决方案,但找不到适合我的问题的解决方案。有人说,如果遇到循环单元引用,则代码设计得很糟糕。
如何改进设计并同时保持较高的计算性能?
解决问题的其他可能性是什么?
非常感谢!
最佳答案
前向声明不能跨单元工作。当一个单元转发声明一个记录/类时,同一单元也必须定义该记录/类。
我建议定义一个 TMesh
实现的 IMesh
接口(interface),然后让 TVertices
和 TTriangleFaces
使用 IMesh
而不是直接使用 TMesh
。这样,就不存在循环引用,并且该接口(interface)可以公开所需的任何字段值的属性。而且 TComponent
禁用已实现接口(interface)的引用计数,因此内存泄漏不是问题。
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.
网格.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
和 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.
网格.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.
创建 TMesh
对象时,请确保代码中某处有一个非弱 IMesh
变量,以便它保持事件状态,直到您不再需要它为止:
var
Meth: IMesh; // or a class member or a global, wherever you need it
Mesh := TMesh.Create;
...
Mesh := nil;
(正确的)引用计数将为您处理剩下的事情。
关于Delphi:更好的设计以避免循环单元引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37174575/
我们已经有一个使用 AnyEvent 的库。它在内部使用 AnyEvent,并最终返回一个值(同步 - 不使用回调)。有什么方法可以将这个库与 Mojolicious 一起使用吗? 它的作用如下: #
我想从 XSD 文件生成带有 JAXB 的 Java 类。 问题是,我总是得到一些像这样的类(删除了命名空间): public static class Action { @X
我有一个关于 html 输入标签或 primefaces p:input 的问题。为什么光标总是自动跳转到输入字段。我的页面高度很高,因此您需要向下滚动。输入字段位于页面末尾,光标自动跳转(加载)到页
我今天在考虑面向对象设计,我想知道是否应该避免 if 语句。我的想法是,在任何需要 if 语句的情况下,您都可以简单地创建两个实现相同方法的对象。这两个方法实现只是原始 if 语句的两个可能的分支。
String graphNameUsed = graphName.getName(); if (graphType.equals("All") || graphType.equals(
我有一张友谊 table CREATE TABLE IF NOT EXISTS `friendList` ( `id` int(10) NOT NULL, `id_friend` int(10
上下文 Debian 64。Core 2 二人组。 摆弄循环。我使用了同一循环的不同变体,但我希望尽可能避免条件分支。 但是,即使我认为它也很难被击败。 我考虑过 SSE 或位移位,但它仍然需要跳转(
我最近在 Java 中创建了一个方法来获取字符串的排列,但是当字符串太长时它会抛出这个错误:java.lang.OutOfMemoryError: Java heap space我确信该方法是有效的,
我正在使用 (C++) 库,其中需要使用流初始化对象。库提供的示例代码使用此代码: // Declare the input stream HfstInputStream *in = NULL; tr
我有一个 SQL 查询,我在 WHERE 子句中使用子查询。然后我需要再次使用相同的子查询将其与不同的列进行比较。 我假设没有办法在子查询之外访问“emp_education_list li”? 我猜
我了解到在 GUI 线程上不允许进行网络操作。对我来说还可以。但是为什么在 Dialog 按钮点击回调上使用这段代码仍然会产生 NetworkOnMainThreadException ? new T
有没有办法避免在函数重定向中使用 if 和硬编码字符串,想法是接收一个字符串并调用适当的函数,可能使用模板/元编程.. #include #include void account() {
我正在尝试避免客户端出现 TIME_WAIT。我连接然后设置 O_NONBLOCK 和 SO_REUSEADDR。我调用 read 直到它返回 0。当 read 返回 0 时,errno 也为 0。我
我正在开发 C++ Qt 应用程序。为了在应用程序或其连接的设备出现故障时帮助用户,程序导出所有内部设置并将它们存储在一个普通文件(目前为 csv)中。然后将此文件发送到公司(例如通过邮件)。 为避免
我有一组具有公共(public)父类(super class)的 POJO。这些存储在 superclass 类型的二维数组中。现在,我想从数组中获取一个对象并使用子类 的方法。这意味着我必须将它们转
在我的代码中,当 List 为 null 时,我通常使用这种方法来避免 for 语句中的 NullPointerException: if (myList != null && myList.size
我正在尝试避免客户端出现 TIME_WAIT。我连接然后设置 O_NONBLOCK 和 SO_REUSEADDR。我调用 read 直到它返回 0。当 read 返回 0 时,errno 也为 0。我
在不支持异常的语言和/或库中,许多/几乎所有函数都会返回一个值,指示其操作成功或失败 - 最著名的例子可能是 UN*X 系统调用,例如 open( ) 或 chdir(),或一些 libc 函数。 无
我尝试按值提取行。 col1 df$col1[col1 == "A"] [1] "A" NA 当然我只想要“A”。如何避免 R 选择 NA 值?顺便说一句,我认为这种行为非常危险,因为很多人都会陷入
我想将两个向量合并到一个数据集中,并将其与函数 mutate 集成为 5 个新列到现有数据集中。这是我的示例代码: vector1% rowwise()%>% mutate(vector2|>
我是一名优秀的程序员,十分优秀!