2016-01-13 13 views
12

Có sự cố khi lưu trữ một mảng trong TQueue. Bất kỳ ý tưởng mà tôi đi sai? Mã hoạt động tốt trong Delphi XE 5 nhưng không ở Delphi 10 Seattle.Có thể lưu trữ mảng trong TQueue không?

(Tôi không thể quyết định nếu điều này là một lỗi hoặc làm thế nào nó cũng làm việc. Cố gắng tìm kiếm các manh mối Embarcadero nhưng thất bại.)

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; 
+1

Ngoài ra, chúng tôi không cần loại mảng byte không tương thích khác. Sử dụng 'TBytes'. Thông thường, sử dụng 'TArray ' cho các loại phần tử khác với 'Byte'. –

+0

Tôi đồng ý. Mảng gốc là TidBytes (Indy) – Hans

+1

Điều gì không hoạt động? –

Trả lời

20

Đây là một khiếm khuyết giới thiệu trong XE8. Đây là bản sao đơn giản nhất mà tôi có thể sản xuất.

{$APPTYPE CONSOLE} 

uses 
    System.Generics.Collections; 

var 
    Queue: TQueue<TArray<Byte>>; 

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

Kết quả là 1 trong XE7 và 0 ở XE8 và Seattle.

Điều này đã được báo cáo cho Embarcadero: RSP-13196.


Việc thực hiện Enqueue trông như thế này:

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; 

Khi T là một mảng động, chi nhánh FQueueHelper.InternalEnqueueMRef được chọn. Điều này lần lượt trông giống như sau:

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; 

Lưu ý rằng không có mục nhập cho TTypeKind.tkDynArray. Bởi vì hai phương pháp này được inlined, inliner quản lý để nén tất cả xuống không có gì. Không có hành động nào được thực hiện khi bạn Enqueue mảng động.

Trở lại trong những ngày tốt đẹp cũ của XE7 mã trông như thế này:

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; 

Không phạm vi cho loại hình khuyết tật đặc biệt ở đó.


Tôi không nghĩ rằng có cách giải quyết dễ dàng cho bạn. Có lẽ cách thuận tiện nhất để tiến hành là lấy mã cho XE7 TQueue và sử dụng nó thay cho việc triển khai bị hỏng từ XE8 và Seattle. Đối với hồ sơ, tôi đã từ bỏ các bộ sưu tập chung của Embarcadero và sử dụng các lớp học của riêng tôi.


Câu chuyện ở đây là ở XE8, Embarcadero quyết định giải quyết sự thiếu hụt trong việc triển khai Generics. Bất cứ khi nào bạn khởi tạo một kiểu generic, các bản sao của tất cả các phương thức được tạo ra. Đối với một số phương pháp, mã giống hệt nhau được tạo cho các lần khởi tạo khác nhau.

Vì vậy, điều này khá phổ biến đối với TGeneric<TFoo>.DoSomethingTGeneric<TBar>.DoSomething để có mã giống hệt nhau. Các trình biên dịch khác cho các ngôn ngữ khác, các mẫu C++, các generic .net, vv, nhận ra sự trùng lặp này và kết hợp các phương thức chung chung giống hệt nhau. Trình biên dịch Delphi không. Kết quả cuối cùng là một tệp thực thi lớn hơn mức cần thiết.

Trong XE8 Embarcadero quyết định giải quyết vấn đề này theo những gì tôi coi là hoàn toàn sai. Thay vì tấn công nguyên nhân gốc rễ của vấn đề, trình biên dịch, họ quyết định thay đổi việc thực hiện các lớp sưu tập chung của họ. Nếu bạn nhìn vào mã trong Generics.Collections, bạn sẽ thấy rằng nó đã được viết lại hoàn toàn trong XE8. Trường hợp trước đó mã từ XE7 và trước đó là có thể đọc được, từ XE8 nó bây giờ là cực kỳ phức tạp và mờ đục.Quyết định này có các hậu quả sau:

  1. Mã phức tạp có nhiều lỗi. Nhiều người trong số này đã được tìm thấy ngay sau khi XE8 được phát hành và đã được cố định. Bạn đã vấp phải một khiếm khuyết khác. Một điều mà chúng tôi đã học được là bộ thử nghiệm nội bộ của Embarcadero không thực hiện đầy đủ các lớp sưu tập của họ. Rõ ràng là các xét nghiệm của họ là không đầy đủ.
  2. Bằng cách thay đổi thư viện của họ thay vì trình biên dịch, họ đã vá các lớp RTL. Vấn đề ban đầu với bloat mã chung vẫn còn cho các lớp bên thứ ba. Đã Embarcadero cố định vấn đề tại nguồn sau đó không chỉ họ có thể đã giữ lại mã lớp bộ sưu tập đơn giản và chính xác từ XE7, nhưng tất cả các mã chung thứ ba sẽ được hưởng lợi.
+1

Cảm ơn bạn đã sắp xếp nó – Hans

Các vấn đề liên quan