2014-01-25 51 views
2

我有一個函數來從字典中彈出元素,如果出現任何錯誤,應拋出異常。代碼看起來相當不錯。W1035:函數'Take'的返回值可能未定義

type 
    ENoSuchElementException = class(Exception); 
var 
    FResults: TDictionary<Cardinal, TObject> = TDictionary<Cardinal, TObject>.Create; 
    FLock: TCriticalSection = TCriticalSection.Create; 

/// <exceptions cref="ENoSuchElementException">Element does not exist</exceptions> 
function Take(Id: Cardinal): TObject; 
begin  
    FLock.Acquire; 
    try 
    try 
     Result := FResults[Id]; // here may throw exception 
     FResults.Remove(Id); 
    except 
     on E: Exception do 
     begin 
     raise ENoSuchElementException.Create(E.ToString); 
     end; 
    end; 
    finally 
    FLock.Release; 
    end; 
end; 

但Delphi XE4編譯器抱怨W1035: Return value of function 'Take' might be undefined

我突然很困惑。如果拋出異常,爲什麼代碼仍然期望返回值?這是否意味着try...finally會吃掉例外?有人可以指出我的代碼問題嗎?

已解決:正如David所說,try...except應該移到外面。謝謝!

+0

感嘆。我們不能有一個SSCCE嗎?現在我們每個人都必須花時間製作一個。 –

+0

@David,我已經更新了代碼示例,現在它應該更加緊湊。 – stanleyxu2005

+0

一個SSCCE真的是需要的 –

回答

1

這似乎是32位編譯器報告的誤報。 64位編譯器不會爲您的代碼報告警告。而64位編譯器是正確的。也許32位編譯器會發現你正在捕獲異常,並且不會繼續檢測到你總是會引發另一個異常。

解決32位編譯器錯誤診斷的一種方法是使try/except成爲最外面的塊。考慮以下SSCCE:

{$APPTYPE CONSOLE} 

uses 
    SysUtils; 

procedure Foo; 
begin 
end; 

function Take1(const Id: Integer): Integer; 
begin 
    try 
    try 
     Foo; 
     Result := 42; 
    except 
     on E:Exception do 
     begin 
     raise Exception.Create(E.ToString); 
     end; 
    end; 
    finally 
    end; 
end; 

function Take2(const Id: Integer): Integer; 
begin 
    try 
    try 
     Foo; 
     Result := 42; 
    finally 
    end; 
    except 
    on E:Exception do 
    begin 
     raise Exception.Create(E.ToString); 
    end; 
    end; 
end; 

begin 
end. 

編譯器的輸出是:

 
[dcc32 Warning] W1035 Return value of function 'Take1' might be undefined 

所以,Take1是我的簡化代碼的版本。 32位編譯器爲此發出警告。並且Take2交換了exceptfinally的訂單。編譯器不會警告。

也許這種解決方法不適合你,但你將不得不提出這種性質的東西。

底線是你的分析是正確的,而編譯器是錯誤的。

+0

是的,我有一個64位編譯器,但構建配置是32位。我有一個自我重寫的VCL庫,它不能以64位模式安裝。我只能在64位模式下編譯它。這就是爲什麼我用32位編譯器構建代碼的原因。 – stanleyxu2005

+0

我不是建議你編譯64位。我沒有寫這個。 –

+0

哦,是的。現在我在代碼中看到了這個問題。我以某種方式將我的大腦與Java/D等聯繫在一起,因爲這些語言有不同的'try ... catch ... finally'處理。 – stanleyxu2005

2

大衛·赫弗南的給你最直接的答案,但另一種方法是完全避免try/except塊和使用TDictionaryTryGetValue方法。去遠一點,你可以,如果你想也擺脫了單獨的鎖定對象:

var FResults: TDictionary<Cardinal, TResult>; 

function Take(Id: Cardinal): TResult; 
begin  
    TMonitor.Enter(FResults); 
    try 
    if FResults.TryGetValue(Id, Result) then 
     FResults.Remove(Id) 
    else 
     Abort; 
    finally 
    TMonitor.Exit(FResults); 
    end; 
end; 

TMonitor歷史上有顯著的錯誤,但它會在XE4是OK。

+1

編譯器是否識別「Abort」?換句話說,在'Abort'提升後,你是否需要在'Abort'後面寫'Result:= nil'。 –

+0

是的,你的方法也適用。但我想利用例外。好處是,在我調用方法的地方,我不必檢查值是否爲空。對我來說,很明顯,一個方法會返回一個非空值或異常。 – stanleyxu2005

+0

我舉一個例子:爲了從列表中獲取元素,我通常會有兩個方法:'getElement(Condition)'和'requireElement(Condition)'。 'get'的意思是,它可能會返回null,但是'require'意味着如果沒有東西可以返回,它會拋出一個異常。我有一個項目https://github.com/stanleyxu2005/dutil/,你可以簽出兩個驗證類,你就會明白。 – stanleyxu2005