gpt4 book ai didi

delphi - 可以将数组存储在 TQueue 中吗?

转载 作者:行者123 更新时间:2023-12-03 14:37:33 26 4
gpt4 key购买 nike

在 TQueue 中存储数组时遇到问题。知道我哪里出错了吗?代码在 Delphi XE 5 中运行良好,但在 Delphi 10 Seattle 中运行不佳。

(我无法确定这是否是一个错误或它应该如何工作。尝试在 embarcadero 中搜索线索但失败了。)

procedure TForm1.Button1Click(Sender: TObject);
var
FData: TQueue<TBytes>;
FsData: TQueue<String>;

arr: TBytes;

begin

FData := TQueue<TBytes>.Create;
FsData := TQueue<String>.Create;
try
setlength(arr, 3);
arr[0] := 1;
arr[1] := 2;
arr[2] := 3;

FData.Enqueue(arr);
Memo1.Lines.Add('Count, array:' + IntToStr(FData.Count)); // 0?

FsData.Enqueue('asada');
Memo1.Lines.Add('Count, string:' + IntToStr(FsData.Count)); // 1
finally
FData.Free;
FsData.Free;
end;
end;

最佳答案

这是 XE8 中引入的缺陷。这是我能制作的最简单的复制品。

{$APPTYPE CONSOLE}

uses
System.Generics.Collections;

var
Queue: TQueue<TArray<Byte>>;

begin
Queue := TQueue<TArray<Byte>>.Create;
Queue.Enqueue(nil);
Writeln(Queue.Count);
end.

XE7 中输出为 1,XE8 和西雅图中输出为 0。

这已报告给 Embarcadero:RSP-13196 .

<小时/>

执行Enqueue看起来像这样:

procedure TQueue<T>.Enqueue(const Value: T);
begin
if IsManagedType(T) then
if (SizeOf(T) = SizeOf(Pointer)) and (GetTypeKind(T) <> tkRecord) then
FQueueHelper.InternalEnqueueMRef(Value, GetTypeKind(T))
else
FQueueHelper.InternalEnqueueManaged(Value)
else
case SizeOf(T) of
1: FQueueHelper.InternalEnqueue1(Value);
2: FQueueHelper.InternalEnqueue2(Value);
4: FQueueHelper.InternalEnqueue4(Value);
8: FQueueHelper.InternalEnqueue8(Value);
else
FQueueHelper.InternalEnqueueN(Value);
end;
end;

何时 T是一个动态数组,FQueueHelper.InternalEnqueueMRef分支被选择。这又看起来像这样:

procedure TQueueHelper.InternalEnqueueMRef(const Value; Kind: TTypeKind);
begin
case Kind of
TTypeKind.tkUString: InternalEnqueueString(Value);
TTypeKind.tkInterface: InternalEnqueueInterface(Value);
{$IF not Defined(NEXTGEN)}
TTypeKind.tkLString: InternalEnqueueAnsiString(Value);
TTypeKind.tkWString: InternalEnqueueWideString(Value);
{$ENDIF}
{$IF Defined(AUTOREFCOUNT)}
TTypeKind.tkClass: InternalEnqueueObject(Value);
{$ENDIF}
end;
end;

请注意,没有 TTypeKind.tkDynArray 的条目。因为这两个方法是内联的,所以内联器设法将其全部压缩为空。当您 Enqueue 时,不会执行任何操作动态数组。

回到 XE7 的美好时光,代码如下所示:

procedure TQueue<T>.Enqueue(const Value: T);
begin
if Count = Length(FItems) then
Grow;
FItems[FHead] := Value;
FHead := (FHead + 1) mod Length(FItems);
Inc(FCount);
Notify(Value, cnAdded);
end;

那里没有类型特定缺陷的范围。

<小时/>

我认为您没有简单的解决方法。也许最方便的方法是获取 XE7 的代码 TQueue并使用它来代替 XE8 和西雅图的损坏实现。根据记录,我已经放弃了 Embarcadero 通用集合并使用我自己的类。

<小时/>

这里的背景故事是,在 XE8 中,Embarcadero 决定解决泛型实现中的缺陷。每当实例化泛型类型时,都会创建所有方法的副本。对于某些方法,为不同的实例化生成相同的代码。

所以 TGeneric<TFoo>.DoSomething 很常见和TGeneric<TBar>.DoSomething具有相同的代码。其他语言、C++ 模板、.net 泛型等的其他编译器可以识别这种重复并将相同的泛型方法合并在一起。 Delphi 编译器没有。最终结果是可执行文件比严格必要的要大。

在 XE8 中,Embarcadero 决定以我认为完全错误的方式解决这个问题。他们没有解决问题的根本原因,即编译器,而是决定更改通用集合类的实现。如果你看一下 Generics.Collections 中的代码,你会看到它在XE8中已经被完全重写了。以前 XE7 及更早版本的代码是可读的,但从 XE8 开始,它现在变得极其复杂且不透明。这一决定产生了以下后果:

  1. 复杂的代码包含许多错误。其中许多问题在 XE8 发布后不久就被发现并已得到修复。您偶然发现了另一个缺陷。我们了解到的一件事是 Embarcadero 的内部测试套件没有充分地测试他们的集合类。很明显,他们的测试是不充分的。
  2. 通过更改库而不是编译器,他们修补了 RTL 类。通用代码膨胀的原始问题仍然存在于第三方类中。如果 Embarcadero 从源头解决了这个问题,那么他们不仅可以保留 XE7 中简单而正确的集合类代码,而且所有第三种通用代码都会受益。

关于delphi - 可以将数组存储在 TQueue 中吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34761496/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com