在将其作为可变参数传递之前转换动态数组是否安全?
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 Byte
和 array of TObject
之间进行转换
- 更微妙的是,您可能会意外地在不兼容对象类型的数组之间进行转换(例如,在
array of TButton
和 array 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 类型之间执行未经检查的转换。
我有一些函数接受 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 Byte
和array of TObject
之间进行转换
- 更微妙的是,您可能会意外地在不兼容对象类型的数组之间进行转换(例如,在
array of TButton
和array 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 类型之间执行未经检查的转换。