這是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和Seattle中輸出爲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它現在是非常複雜和不透明的。該決定產生以下後果:
- 複雜的代碼包含許多錯誤。其中許多是在XE8發佈並被修復後不久發現的。你已經偶然發現了另一個缺陷。我們所學到的一件事是Embarcadero的內部測試套件不能充分運用他們的集合類。顯然他們的測試是不夠的。
- 通過改變他們的庫而不是編譯器,他們修補了RTL類。通用代碼膨脹的原始問題仍然適用於第三方類。如果Embarcadero從源頭上解決了這個問題,那麼他們不僅可以保留來自XE7的簡單而正確的集合類代碼,而且所有第三個通用代碼都會受益。
此外,我們不需要另一個不兼容的字節數組類型。使用'TBytes'。對於「Byte」以外的元素類型,更一般地使用'TArray'。 –
我同意。原始數組是TidBytes(Indy) – Hans
什麼不起作用? –