これは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
の実装は次のようになりますT
ダイナミックアレイの場合
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;
、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
のエントリがないこと:これは、順番に次のようになります。これらの2つのメソッドはインライン展開されているため、インライナはそれをすべて圧縮して無駄にします。 Enqueue
ダイナミックアレイの場合は何も実行されません。そこタイプ特定の欠陥のために
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の古き良き時代のコードはこのように見えました。
私は簡単な回避策はないと思います。 XE7 TQueue
のコードをとり、XE8とSeattleの壊れた実装の代わりにそれを使用するのが最も適切な方法でしょう。記録のために、私はEmbarcaderoのジェネリックコレクションをあきらめ、自分のクラスを使用しました。ここ
バックストーリーがXE8で、エンバカデロがジェネリック医薬品のその実装の不足に対処することを決定したということです。ジェネリック型をインスタンス化すると、すべてのメソッドのコピーが作成されます。いくつかの方法では、異なるインスタンス化に対して同一のコードが生成される。
TGeneric<TFoo>.DoSomething
とTGeneric<TBar>.DoSomething
は同じコードを持つことがよくあります。他の言語用の他のコンパイラ、C++テンプレート、.netジェネリックなどは、この複製を認識し、同じジェネリックメソッドを一緒にマージします。 Delphiコンパイラはそうではありません。最終結果は、厳密に必要な実行可能ファイルよりも大きい実行可能ファイルです。
XE8では、エンバカデロはこれについて、私が間違った方法でこれに取り組むことに決めました。コンパイラは、問題の根本原因を攻撃する代わりに、ジェネリックコレクションクラスの実装を変更することに決めました。 Generics.Collections
のコードを見ると、XE8で完全に書き直されていることがわかります。以前はXE7以前のコードが読めるところでは、XE8から非常に複雑で不透明になっています。この決定には次のような結果がありました。
- 複合コードには多くのエラーが含まれていました。これらの多くは、XE8がリリースされた直後に発見され、修正されました。あなたは別の欠陥を見つけました。私たちが学んだことの1つは、Embarcaderoの内部テストスイートがコレクションクラスを十分に実行していないことです。彼らのテストが不十分であることは明らかに明らかです。
- コンパイラではなくライブラリを変更することにより、RTLクラスにパッチを適用しました。ジェネリックコードの膨大な問題は、サードパーティのクラスに残っています。 Embarcaderoがソースで問題を修正した場合、XE7からシンプルで正確なコレクションクラスコードを保持できるだけでなく、すべての3番目の汎用コードが恩恵を受けていました。
他にも、互換性のない別のバイト配列型は必要ありません。 'TBytes'を使用してください。より一般的には、Byte以外の要素型には 'TArray'を使用します。 –
私は同意します。元の配列はTidBytes(Indy)です – Hans
何がうまくいかないのですか? –