Questo è un difetto introdotto nel XE8. Ecco la riproduzione più semplice che posso produrre.
{$APPTYPE CONSOLE}
uses
System.Generics.Collections;
var
Queue: TQueue<TArray<Byte>>;
begin
Queue := TQueue<TArray<Byte>>.Create;
Queue.Enqueue(nil);
Writeln(Queue.Count);
end.
L'uscita è 1 in XE7 e 0 in XE8 e Seattle.
Questo è già stato segnalato a Embarcadero: RSP-13196.
L'attuazione Enqueue
aspetto:
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;
Quando T
è un array dinamico, il ramo FQueueHelper.InternalEnqueueMRef
è scelto. Questo a sua volta si presenta così:
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;
Si noti che non v'è alcuna voce per TTypeKind.tkDynArray
. Poiché questi due metodi sono in linea, l'inliner riesce a comprimere tutto fino al nulla. Nessuna azione viene eseguita quando si imposta un array dinamico Enqueue
.
Torna nei bei vecchi tempi di XE7 il codice si presentava così:
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;
Nessuna possibilità di difetti specifici di tipo lì.
Non penso che ci sia una soluzione facile per te. Forse il modo più conveniente per procedere è prendere il codice per XE7 TQueue
e utilizzarlo al posto dell'implementazione non funzionante da XE8 e Seattle. Per la cronaca, ho rinunciato alle collezioni generiche di Embarcadero e uso le mie lezioni.
La parte posteriore storia qui è che in XE8, Embarcadero ha deciso di affrontare una carenza nella loro attuazione dei farmaci generici. Ogni volta che istanziate un tipo generico, vengono create copie di tutti i metodi. Per alcuni metodi, viene generato un codice identico per diverse istanze.
Quindi è abbastanza comune per TGeneric<TFoo>.DoSomething
e TGeneric<TBar>.DoSomething
avere il codice identico. Altri compilatori per altre lingue, modelli C++, generici .net, ecc. Riconoscono questa duplicazione e uniscono insieme metodi identici generici. Il compilatore Delphi no. Il risultato finale è un eseguibile più grande di quello strettamente necessario.
In XE8 Embarcadero ha deciso di affrontare questo in quello che considero assolutamente sbagliato. Invece di attaccare la causa principale del problema, il compilatore, decisero di cambiare l'implementazione delle loro classi di raccolta generiche. Se si guarda il codice in Generics.Collections
, si vedrà che è stato completamente riscritto in XE8. Laddove prima era leggibile il codice da XE7 e precedenti, da XE8 ora è estremamente complesso e opaco.Questa decisione ha avuto le seguenti conseguenze:
- Il codice complesso conteneva molti errori. Molti di questi sono stati trovati poco dopo che XE8 è stato rilasciato e risolto. Sei incappato in un altro difetto. Una cosa che abbiamo imparato è che la suite di test interna di Embarcadero non esercita le loro classi di raccolta sufficientemente. È palesemente chiaro che i loro test sono inadeguati.
- Modificando la libreria piuttosto che il compilatore, hanno corretto le classi RTL. Il problema originale con il bloat generico del codice rimane per le classi di terze parti. Se Embarcadero avesse risolto il problema all'origine, non solo avrebbero potuto mantenere il codice della classe di raccolta semplice e corretto di XE7, ma tutto il terzo codice generico ne avrebbe beneficiato.
A parte questo, non è necessario un altro tipo di array di byte incompatibile. Usa 'TBytes'. Più in generale usa 'TArray' per tipi di elementi diversi da 'Byte'. –
Sono d'accordo. L'array di origine è TidBytes (Indy) – Hans
Che cosa non funziona? –