用TArray<XXX>替换数组XXX是安全的吗?

13

我声明了相当多的变量为

var
  Something: array of XXX;
begin
  SetLength(Something, 10);
  try
    ...
  finally
    SetLength(Something, 0);
  end;
end;

更换它们到什么程度才是安全的:

var
  Something: TArray<XXX>;
begin
  SetLength(Something, 10);
  try
    ...
  finally
    SetLength(Something, 0);
  end;
end;

4
注意:你不需要使用 try/finally 来释放动态数组类型的内存。它们是引用计数类型,只要 Something 超出作用域,内存就会自动释放,除非它们仍被另一个变量引用。无论哪种方式,它的行为与将其长度设置为零一样。 - user743382
2个回答

12

如已回答,TArray<XXX> 就像任何其他自定义类型一样,被定义为 array of XXX。实际上,TArray<XXX> 是一个被定义为 array of XXX 的自定义类型。

话虽如此,在过程或函数参数的上下文中,一个被定义为 array of XXX 的自定义类型与 array of XXX 并不等同。在 procedure Foo(x: array of Integer) 中,x 是一个 开放数组参数,可以接受任何类型的整数数组。相比之下,procedure Foo(x: TArray<Integer>) 只接受实际的 TArray<Integer> 类型。当尝试传递固定大小的数组时,也可以看到差异,但是当尝试传递 TDynIntegerArray(另一种类型,也被定义为 array of Integer)时,也可以看到差异。

所以,对于变量来说,如果你有一个array of XXX,想改成TArray<XXX>也没问题。只要确保不要进行全局搜索和替换。


+1 用于区分变量和方法参数。但是,即使对于方法参数,除非您知道并分析代码,否则也不能为其中之一做出决定。虽然您可以将 TArray<XXX> 传递给 array of XXX,但您无法将 array of XXX 传递给 TArray<XXX>。因此,在某些情况下,您可能会将 TArray<XXX> 的参数设置为,而在其他情况下,您可能希望将其保留为 array of XXX - Stefan Glienke
我不同意第一段。这些类型并不相同。它们是不同的类型,不能进行赋值兼容。 - David Heffernan
@DavidHeffernan 在第一段中,我的措辞只有一个小问题。第二段有一个更大的错误。我编辑了我的答案来解决这个问题。 - user743382
2
@DavidHeffernan 这只是因为TArray<T>恰好在一个对第三方库都可用的单元中定义(在您的示例中)。它是泛型的事实并不相关。两个第三方库同样可以添加对JCL的依赖,并使用JclBase.TDynIntegerArray。无论如何,这不是问题所在,我认为我们偏离了主题。 - user743382
1
你们知道 Types.pas 中声明了 TIntegerDynArray 和几乎所有内置类型的其他 TXXXDynArray 类型吗?对于大多数内置类型,甚至没有 TArray<T>,也不需要依赖第三方库。 - Stefan Glienke
显示剩余14条评论

5

这样做是完全安全的。编译器会生成相同的输出。

个人而言,在其他条件相等的情况下,我建议这样做。使用通用数组 TArray<T> 可以使您拥有更大的灵活性和更好的类型兼容性。

当然,这些优点只能在执行一些工作的更现实的代码中看到。通常在使用通用容器时才能看到好处。但是,在尝试构建使用多个不同库的代码时,您也可能会看到好处。

使用通用数组可以轻松实现类型兼容性。在通用数组之前,您需要像这样定义一个数组类型:

TIntArray = array of Integer;

如果两个库这样做,那么它们就是不兼容的类型。如果库都同意使用通用数组,则将具有兼容性。
更清晰地了解这一点,请考虑以下片段:
type
  TIntArray1 = array of Integer;
  TIntArray2 = array of Integer;
....
var
  arr1: TIntArray1;
  arr2: TIntArray2;
....
arr1 := arr2;

这个赋值操作是无效的,并且会导致类型不匹配的编译器错误。在Pascal语言中,这完全是可以预料的。毕竟,它是一种强类型语言,这些类型是独立的。即使它们实现方式相同。

另一方面:

var
  arr1: TArray<Integer>;
  arr2: TArray<Integer>;
....
arr1 := arr2;

这段代码是有效的并且可以编译。关于泛型类型兼容性的文档指出:

如果基本类型相同(或者是别名),并且类型参数相同,那么两个实例化的泛型就被认为是赋值兼容的。


1
编译器将生成相同的输出...除非您关心RTTI,否则TArray<T>可能会失败。(除非在更新版本中已经修复了这个问题?) - Mason Wheeler
2
@MasonWheeler,没错!例如Datasnap使用的JSON marshaling可以完美处理声明为array of T的字段类型,但无法处理TArray<T>。因此,像往常一样,答案是:这取决于具体情况。 - Uwe Raabe
1
@UweRaabe RTTI 可以很好地读取 TArray<T> 字段(即使在 Delphi 2010 中),并正确返回 TRttiDynamicArrayType 作为其 FieldType。它的问题实际上是与 array of T 相反,因为这不是预定义类型。 - Stefan Glienke
2
@StefanGlienke,我快速检查了一下使用TJSONConverters.GetJSONMarshaler.Marshal将具有字段if TIntArray = array of Integer和TDynArray = TArray<Integer>的对象进行编组:XE5和XE6可以很好地完成,但XE3无法完成。 - Uwe Raabe
@StefanGlienke 感谢您的评论。 - David Heffernan
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接