在将其作为可变参数传递之前转换动态数组是否安全?

Is safe to cast a dynamic array before passing it as a variable parameter?

我有一些函数接受 TObject 数组作为 var 参数,例如:

type
  TObjectArray = array of TObject;

...

procedure DeleteAt(var AArray : TObjectArray; AIndex : integer);
begin
  while(AIndex < Length(AArray) - 1) do
  begin
    AArray[AIndex] := AArray[AIndex + 1];
    Inc(AIndex);
  end;
  SetLength(AArray, Length(AArray) - 1);
end;

为了重用相同的函数,我将 TObject 的后代 类 的几个动态数组转换为 TObjectArray,然后再将它们传递给我的函数,例如:

var
  Buttons : array of TButton;
begin
  SetLength(Buttons, 1);
  Buttons[0] := Button1;

  //...

  DeleteAt(TObjectArray(Buttons), 0);
end;

这是一种安全的做法还是会导致一些我没有考虑到的问题?

只要阵列兼容,该技术本身就不会造成伤害。 array of TObject 的成员只是对对象的引用。由于可以从祖先引用安全地使用对象,因此不存在固有问题。

当然,您在问题中显示的代码是安全的。如果就您的所有权模式而言有意义,您甚至可以 Free 您是 removing/deleting 的对象实例。


也就是说,存在编译器无法执行其任何典型类型安全检查的风险。

  • 在极端情况下,您可以在 array of Bytearray of TObject
  • 之间进行转换
  • 更微妙的是,您可能会意外地在不兼容对象类型的数组之间进行转换(例如,在 array of TButtonarray of TQuery 之间进行转换)。在处理单个对象时,您可以选择使用 (<object> as <type>) 检查 type-cast。这将执行 compile-time 检查以验证类型是否在同一层次结构中(否则强制转换保证不兼容),并执行 run-time 检查以确保对象实例实际上是正确的类型。
  • 虽然删除和交换数组中的对象是安全的,但 var 引用意味着您可以还可以添加 对象到阵列。现在,array of TButton 会强制您添加 TButton(或 subclass)实例;将类型转换为 array of TObject 意味着您可能会不小心将任何其他对象添加到数组中。当函数 returns 时,代码将尝试使用对象 非常不正确 。您几乎肯定会遇到奇怪的行为 and/or 访问冲突。由于 根本问题 已在程序的较早部分悄悄引入,因此它们将更难调试。

结论

虽然该技术有效,但建议您使用 TObjectList

  • 这保留了基于 type-checking 的标准对象。
  • 它支持类似于数组的使用,因此通常是一个很好的替代品。
  • 作为额外的好处,容器可以在您添加和删除项目时动态调整大小 - 不再需要手动调整大小。
  • 唯一的小不便是您必须创建和销毁 TObjectList

即使 TList 通常也是更好的选择,尽管您必须在 Pointer 和 class 类型之间执行未经检查的转换。