2017-09-28 26 views
12

Tìm thấy xử lý ngoại lệ trong Delphi Tokyo hoạt động hơi khác so với các phiên bản Delphi trước đó.Ngoại lệ Delphi Tokyo ngăn thiết lập kết quả chức năng

function FuncTest: integer; 
begin 
    Result := 1; 
    try 
    raise Exception.Create('Error Message'); 
    finally 
    Result := 2; 
    end; 
end; 

function Test:integer; 
begin 
    Result:=0; 
    try 
    Result:=FuncTest; 
    finally 
    ShowMessage(Result.ToString); 
    end; 
end; 

Trong phiên bản Delphi hộp thông báo hiển thị ở đây "2", Tokyo - "0". Đây có phải là lỗi Tokyo hay ngoại lệ không nên được xử lý như thế này?

Trả lời

10

Hành vi của Tokyo là chính xác. Một hàm làm tăng một ngoại lệ không trả về một giá trị. Bạn đã từng dựa vào chi tiết triển khai.

xem xét mã này:

Result:=FuncTest; 

này thực hiện như sau:

  1. FuncTest được gọi.
  2. Result được chỉ định.

Bây giờ, vì bước 1 đưa ra ngoại lệ, bước 2 không thực thi.

Nếu có bất cứ điều gì, tôi sẽ nói rằng hành vi bạn báo cáo từ các phiên bản trước đó không rõ ràng. Trong chức năng này:

function Test:integer; 
begin 
    Result:=0; 
    try 
    Result:=FuncTest; 
    finally 
    ShowMessage(Result.ToString); 
    end; 
end; 

Tuyên bố Result:=FuncTest đặt ra một ngoại lệ và vì vậy Result không nên được sửa đổi bởi tuyên bố đó. Một cách khác để nghĩ về nó là hàm được gọi nhưng phép gán không được thực thi.


Một trong những vấn đề với ABI Delphi là các giá trị trả về hàm đôi khi được thực hiện như tham số ngụ ý var. Điều đó có nghĩa là việc chuyển nhượng có thể hoặc không thể xảy ra. Để chứng minh:

{$APPTYPE CONSOLE} 

uses 
    System.SysUtils; 

type 
    TRec1 = record 
    X1: NativeInt; 
    end; 

    TRec2 = record 
    X1: NativeInt; 
    X2: NativeInt; 
    end; 

function GetRec1: TRec1; 
begin 
    Result.X1 := 1; 
    raise Exception.Create(''); 
end; 

function GetRec2: TRec2; 
begin 
    Result.X1 := 1; 
    raise Exception.Create(''); 
end; 

procedure Main; 
var 
    Rec1: TRec1; 
    Rec2: TRec2; 
begin 
    Rec1 := Default(TRec1); 
    Writeln(Rec1.X1); 
    try 
    Rec1 := GetRec1; 
    except 
    end; 
    Writeln(Rec1.X1); 

    Rec2 := Default(TRec2); 
    Writeln(Rec2.X1); 
    try 
    Rec2 := GetRec2; 
    except 
    end; 
    Writeln(Rec2.X1); 
end; 

begin 
    Main; 
    Readln; 
end. 

đầu ra này:

 
0 
0 
0 
1 

Đó là khá thất vọng. Không nên sửa đổi biến số của người gọi, nhưng việc sử dụng tham số ngụ ý var thay vì trả về giá trị cho phép sự rò rỉ này. Theo quan điểm của tôi, đây là một lỗ hổng nghiêm trọng trong thiết kế của Delphi ABI, một lỗ hổng mà bạn sẽ không tìm thấy trong hầu hết các ngôn ngữ khác.


Trong mã của bạn, không có thông số var vì loại trả về được chuyển vào sổ đăng ký. Trong trường hợp đó, bất kỳ phiên bản Delphi nào xuất ra 2 bị hỏng.

Về cơ bản, mã của bạn bị nhầm lẫn với kỳ vọng của nó. Nếu một hàm làm tăng một ngoại lệ thì bạn phải giả định rằng giá trị trả về là không xác định.

Cuối cùng, mã của bạn sẽ xuất ra 0 trong XE3 và XE7, vì vậy, tôi tự hỏi bạn cần đi bao xa để xem giá trị 2.

+0

Tôi không có phiên bản Delphi trước đó, nhưng tôi nghĩ đó là XE2, nơi trình biên dịch x64 được giới thiệu.A có một mã dựa trên thực tế là FuncTest trả về hoặc 1 (không thử khối) hoặc 2. Ok tôi đã nhận nó, không có nhiều chức năng nâng cao ngoại lệ, chỉ có thủ tục. – Molochnik

+1

Sẽ tốt cho một chức năng để tăng ngoại lệ. Có gì sai khi mong đợi một giá trị được trả lại trong trường hợp đó. Trong trường hợp này, vì kiểu trả về phù hợp trong thanh ghi, ABI là tốt, không có các vấn đề param 'var'. Nhưng có, XE2 64 bit đã có một vài vấn đề tốt. –

+0

Bạn có nghĩa là nếu hàm trả về e, g, một bản ghi thì sẽ không có vấn đề gì? – Molochnik

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