如何在delphi中在运行时创建记录

如何在delphi中在运行时创建记录,delphi,firemonkey,Delphi,Firemonkey,我想创建一个在运行时调用TSprite的记录 TSprite是我正在构建的关卡编辑器中使用的图像和8个选择点 type TSprite = record Image: TImage; Selection: TSelection; SelectionPointTL: TSelectionPoint; // top-left SelectionPointTM: TSelectionPoint; // top-middle SelectionPointTR

我想创建一个在运行时调用
TSprite
的记录

TSprite
是我正在构建的关卡编辑器中使用的图像和8个选择点

type 
  TSprite = record
    Image: TImage;
    Selection: TSelection;
    SelectionPointTL: TSelectionPoint; // top-left
    SelectionPointTM: TSelectionPoint; // top-middle
    SelectionPointTR: TSelectionPoint; // top-right
    SelectionPointML: TSelectionPoint; // middle-left
    SelectionPointMR: TSelectionPoint; // middle-right
    SelectionPointBL: TSelectionPoint; // bottom-left
    SelectionPointBM: TSelectionPoint; // bottom-middle
    SelectionPointBR: TSelectionPoint; // bottom-right
  end;
现在我想把它存储在一个数组中

arrSprites: array[0..1000] of TSprite;
现在是创作(我正在努力的部分)

这就是我到目前为止所做的:

arrSprites[i].Image.Position.X := frmMainUI.CurrentMouseX;
arrSprites[i].Image.Position.Y := frmMainUI.CurrentMouseY;
arrSprites[i].Image.Bitmap.LoadFromFile('1.png');
arrSprites[i].Image.Visible := True;
arrSprites[i].Image.WrapMode := TImageWrapMode.iwStretch;
因此,这段代码要做的是在滚动框i name:fsbcanvas中创建一个图像,周围有一个选择

为了清楚起见,我需要创建
TSprite
实例的代码


谢谢

不需要创建记录。它们是值类型,您应该像对待其他值类型一样看待它们,例如
Integer
。声明一个局部变量或一个值类型的类字段,这就是您所需要做的。类似地,恒定大小的数组也是一种值类型

因此,您的问题的答案是,
arrspites
不需要任何特殊分配。需要分配和初始化的是记录的内容。因此,如果记录中的任何字段都是类实例,那么它们需要实例化。所以,考虑这个记录:

type
  TMyRecord = record
    i: Integer;
    obj: TObject;
  end;
您可以这样声明一个:

var
  rec: TMyRecord;
并且记录本身被分配。但您需要初始化其成员:

rec.i := 42; // or some other initial value
rec.obj := TObject.Create; // instantiate the object
当你处理完记录后,你需要销毁这个对象

rec.obj.Free;
这很容易出错,因此通常情况下,您的记录应该只包含值类型或托管类型(例如字符串、接口、动态数组等)

现在,我无法从问题中看出您的代码是关于什么的,但我怀疑您的记录中有一些类实例。这立即使记录成为一种可疑的数据结构选择。我会将它们保存在一个类中,该类具有构造函数和析构函数,用于管理其中对象的生存期


我还避免使用等长数组。他们非常死板。相反,我建议您将精灵对象保存在一个通用列表中,
TList
。或者,也许更好的是,
TObjectList
,这样你就可以让列表照顾到其成员的生命周期。

当David Heffernan抢先回答时,我正在键入答案。 尽管如此,我想补充一点

请注意,在第一个代码示例中,您正在使用记录(TSprite)来保存对象(TImage)。TImage实际上是一个视觉组件,但最终它从TObject派生而来

记录与大多数变量(如整数)类似,因为它不必实例化,可以随意复制

然而,对象是指向实例的指针,必须按照以下步骤创建/销毁

rec.Image := TImage.Create(nil);
// do other things..
rec.Image.Free;
因此,如果操作不当,可能会导致内存泄漏或访问冲突错误。(例如,在复制TSprite时…) 在这个设置中有很多事情可能出错,所以我说

在记录中使用对象可能会变得棘手。考虑将对象保存在对象中: 一个简单的解决方案(如果您确实希望在TSprite中保留TImage的实例或指针引用)是使TSprite也成为一个对象。它可以通过使用构造函数和析构函数自动跟踪创建/销毁:

TSpriteObject = class(TObject)
public
  Image : TImage;

  constructor Create;
  destructor  Destroy; override;
end;
及其实施:

constructor TSpriteObject.Create;
begin
  Image := TImage.Create(nil);
  // ^ TImage is a component and expects an Owner component that would also
  //   destroy it, so we use a nil value to disable that behavior.
end;

destructor TSpriteObject.Destroy;
begin
  Image.Free;
end;
然后,您可以让TObjectList跟踪TSprite的多个实例,当列表被清除或销毁时,它将销毁任何TSprite(这将销毁其时间)


(这是我第一次尝试stackoverflow post,请耐心等待,我会发现我在这里做错了什么)

正如David Heffernan所说,对于这种情况,类可能是更好的数据结构。但是,如果您决定改用记录,您可以在记录中声明方法来重构实例化和销毁部分:

type
  TSprite = record
    Image: TImage;
    Selection: TSelection;
    SelectionPointTL: TSelectionPoint; // top-left
    SelectionPointTM: TSelectionPoint; // top-middle
    SelectionPointTR: TSelectionPoint; // top-right
    SelectionPointML: TSelectionPoint; // middle-left
    SelectionPointMR: TSelectionPoint; // middle-right
    SelectionPointBL: TSelectionPoint; // bottom-left
    SelectionPointBM: TSelectionPoint; // bottom-middle
    SelectionPointBR: TSelectionPoint; // bottom-right
    procedure Create(aImageOwner: TComponent);
    procedure Destroy;
  end;

{ TSprite }

procedure TSprite.Create(aImageOwner: TComponent);
begin
  Image := TImage.Create(aImageOwner);
end;

procedure TSprite.Destroy;

begin
  Image.Free;
end;

// ...

var
  Rec: TSprite;
begin
  Rec.Create(Form1);

  // ...

  Rec.Destroy;
end;

注意,它不是一个真正的类实例化,所以
Rec:=TSprite.Create(Form1)TSprite,否则代码>将毫无意义。以这种方式创建

TSprite
不需要创建<另一方面,code>TImage
。您没有显示足够的代码。Delphi中的记录是一种值类型,您不需要创建它。你可以声明一个指向记录的指针并创建一个指针引用的记录实例,但你的问题并不清楚你真正需要什么。我完全不知道第一段代码是用什么语言编写的……你帮了很多忙,我会看看我是否能编辑这个问题,使其更有意义。这个答案与我的答案非常相似。我认为这只是一个巧合,因为毕竟,我们双方的观点是显而易见的。我注意到您的析构函数需要标记为
override
。您应该考虑调用构造函数/析构函数中的继承。这一直是个好习惯。你是说熊而不是光着身子。如果你同意的话,我就不会在这里暴露任何东西了!;-)谢谢科苏斯,大卫,更新了我的答案。我同意你的答案并非巧合,在键入我的答案时,我删除了几段,因为你更好地涵盖了这一点。我回答的目的是指出引用对象的记录的内存泄漏和AV危险。