Han*_*ans 13 delphi delphi-10-seattle
在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;
Run Code Online (Sandbox Code Playgroud)
Dav*_*nan 20
这是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.
Run Code Online (Sandbox Code Playgroud)
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;
Run Code Online (Sandbox Code Playgroud)
何时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;
Run Code Online (Sandbox Code Playgroud)
请注意,没有条目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;
Run Code Online (Sandbox Code Playgroud)
那里没有类型特定缺陷的范围.
我不认为你有一个简单的解决方法.也许最有效的方法是获取XE7的代码TQueue并使用它来代替XE8和Seattle的破坏实现.为了记录,我放弃了Embarcadero通用集合并使用我自己的类.
这里的背后故事是,在XE8中,Embarcadero决定解决其仿制药实施的不足.每当您实例化泛型类型时,都会创建所有方法的副本.对于某些方法,为不同的实例化生成相同的代码.
所以这是很常见的TGeneric<TFoo>.DoSomething,并TGeneric<TBar>.DoSomething具有相同的代码.其他语言的其他编译器,C++模板,.net泛型等,识别这种重复并将相同的通用方法合并在一起.Delphi编译器没有.最终结果是一个比严格必要的更大的可执行文件.
在XE8中,Embarcadero决定以我认为完全错误的方式解决这个问题.他们没有攻击问题的根本原因,而是决定改变其泛型集合类的实现.如果查看代码Generics.Collections,您会发现它已在XE8中完全重写.以前XE7及更早版本的代码是可读的,从XE8开始,它现在非常复杂和不透明.该决定产生以下后果: