如何调整我的代码以适配TBytes和TIdBytes之间的兼容性?

7
我遇到了 "Delphi XE4 Indy compatibility issue between TBytes and TidBytes " 中提到的同样问题,即在使用 Delphi XE4 编译时,TBytes(Delphi RTL) 和 TIdBytes(Indy) 数据类型之间存在兼容性问题。我的问题源于代码不完全符合 Indy 的接口,并且一些函数在调用本地 Indy IO 过程时使用了 TBytes 而不是 TIdBytes。
所以我想知道最好的解决方法是什么?
我认为有两种方法:
  1. 重构项目中所有函数,改用 TIdBytes 而不是 TBytes。
  2. 实现一个 TBytesToTidBytes 转换过程(将 TBytes 转换为 TIdBytes),并在进行上述本地 Indy 调用之前调用该过程。
哪种方法更好/最好?您有其他想法吗?

提醒:我正尝试通过XE4配置的项目可以在线上sourceforge找到: http://sourceforge.net/projects/indy10clieservr/?source=directory

建议的转换步骤应该类似于:

procedure TBytesToTIdBytes(const Input:TBytes, var Output: TIdBytes)
var 
    i,L : Integer;
    allocate : Boolean;
begin
    L := Length(Input);
    if(Length(Output) <> L) then 
    begin 
        SetLength(Output,L);
    end;
    if(L > 0) then 
        move(Pointer(Input)^,Pointer(Output)^,L);
end;

你的 allocate 变量没有任何作用,但是你的长度检查是危险的。如果数组具有相同的长度,则不会重新分配数组,但是如果目标数组的引用计数大于一,则最终会覆盖你可能不想要的数组。持有对数组的其他引用的代码可能会继续期望原始数据。更安全的做法是无条件地重新分配。 - Rob Kennedy
我能理解你的观点。但是你为什么认为“allocate”变量是无用的呢?它旨在指示目标数组是否已被预先分配。 - kenny
没什么用,因为唯一会影响是否调用 SetLength 的时候,调用 SetLength 没有实际效果。只有在 allocate 为真时,才会因其值导致调用 SetLength。只有当 Output 为空数组时,allocate 才为真,这意味着它是一个空数组。如果 Input 是空数组,则不需要调用 SetLength,因为它只会使 Output 变为空的,而我们已经知道它本来就是空的。如果 Input 不为空,则长度比较将会评估为 true,即使没有检查 allocate 的值,也将会导致调用 SetLength - Rob Kennedy
已经注意到了!然而,由于我正在处理大量数据,我认为更简单的类型转换解决方案更可取。谢谢。 - kenny
作为解决数据类型不兼容的替代方案,您可以声明函数接收开放数组中的数据。 - David Heffernan
2个回答

7

TBytesTIdBytes都实现为动态数组,只是声明方式不同。 "政治正确"的解决方案是复制字节。但对于大型数组,这可能会浪费内存。更简单的解决方案是使用类型转换,以便可以利用数组的内部引用计数,例如:

type
  PIdBytes = ^TIdBytes;
var
  B1: TBytes;
  B2: TIdBytes;
begin
  B1 := ...;
  B2 := PIdBytes(@B1)^;
end;

或者简单地说:
var
  B1: TBytes;
  B2: TIdBytes;
begin
  B1 := ...;
  B2 := TIdBytes(B1);
end;

为什么不直接使用 B2 := TIdBytes(B1) 呢? - David Heffernan
那也可以。我有 C++ 的背景,所以在类型转换中更喜欢使用指针。 - Remy Lebeau

1

这两种类型在实现层面上并不相同,在较新的 Delphi 版本中(TBytes 在最近的 Delphi 发布版中是 TArray<Byte> 的简单别名)。

因此,我猜您可以使用这样的过程:

procedure TBytesToTIdBytes(const Input: TBytes; var Output: TIdBytes);
var L: integer;
begin
  L := Length(Input);
  SetLength(Output,L);
  move(Input[0],Output[0],L);
end;

这里的move()比循环更快。

在调用 Move() 之前,请确保检查 L > 0,以避免出现越界错误。 - Remy Lebeau
或者使用 move(Pointer(Intput)^,Pointer(Output)^,L) - David Heffernan
@RemyLebeau 只有在启用了范围检查时才会发生OOB错误。上面的代码将在默认编译器设置下运行良好,没有任何问题。 - Arnaud Bouchez
@ArnaudBouchez:现在保险起见总比以后后悔好。防御性编程是你的朋友。 - Remy Lebeau

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