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

时间:2013-02-21 14:38:14

标签: delphi firemonkey

我想创建一个在运行时调用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;

所以这段代码应该做的是在一个名为fsbcanvas的滚动框内创建一个带有选择的图像。

要明确我要求创建TSprite

实例的代码

由于

3 个答案:

答案 0 :(得分:8)

我正在输入答案,因为David Heffernan只是打败了我。 还是在它的时候我想补充一下;

意识到在你的第一个代码示例中,您正在使用记录(TSprite)来保存对象(TImage)。 TImage实际上是一个可视组件,但最终它来自TObject。

记录与大多数变量(如Integer)一样,因为它不需要实例化,可以随意复制。

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

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(它会破坏其TImage)。

(这是我第一次尝试stackoverflow帖子,请在我发现我在这里做错的时候请耐心等待)

答案 1 :(得分:7)

不需要创建记录。它们是值类型,您应该以与考虑其他值类型相同的方式来考虑它们,例如: Integer。声明一个局部变量或一个值类型的类字段,这就是你需要做的。类似地,常量大小的数组是值类型。

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

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<T>中。或者,甚至更好,TObjectList<T>,这样你就可以让列表照顾其成员的生命时间。

答案 2 :(得分:1)

正如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.Create