2014-01-25 3 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 будет есть исключение? Может ли кто-нибудь указать на проблему моего кода?

ПОСТАНОВИЛ: Как указывал Дэвид, try...except следует переместить на наружную сторону. Благодаря!

+0

Вздох. Разве у нас не было SSCCE? Теперь каждый из нас должен тратить время на создание. –

+0

@ Давид, я обновил пример кода, теперь он должен быть более компактным. – 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 своп заказа except и finally. И компилятор не предупреждает.

Возможно, это обходное решение не для вас, но вам придется придумать что-то подобное.

Суть в том, что ваш анализ правильный, а компилятор ошибочен.

+0

Да, у меня 64-битный компилятор, однако конфигурация сборки 32 бит. У меня есть собственная перезаписанная библиотека VCL, которая никогда не может быть установлена ​​в режиме 64 бит. Я могу только скомпилировать его в режиме 64 бит. Именно по этой причине я строю свой код с 32-битным компилятором. – stanleyxu2005

+0

Я не предлагаю вам компилятор для 64 бит. Я этого не писал. –

+0

О да. Теперь я вижу проблему в своем коде. Я каким-то образом связал свой мозг с Java/D и т. Д., Так как эти языки имеют другую обработку 'try ... catch ... finally'. – stanleyxu2005

2

Дэвид Heffernan дал вам самый прямой ответ, однако альтернативой является полностью избежать колодку try/except и использовать TryGetValue метод TDictionary. Пройдя немного дальше, вы можете, если вы хотите, чтобы также избавиться от отдельного объекта запирающего:

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.

+1

Не распознает ли компилятор 'Abort'? Другими словами, вам нужно написать 'Result: = nil' после' Abort', учитывая, что 'Abort' поднимается. –

+0

Да, ваш подход также работает. Но я хочу воспользоваться преимуществами исключений. Преимущество в том, что, когда я вызываю метод, мне не нужно проверять, является ли значение нулевым. Для меня это будет совершенно ясно, метод вернет ненулевое значение или исключение. – stanleyxu2005

+0

Я приведу вам пример: чтобы получить элемент из списка, я обычно буду иметь два метода: 'getElement (Условие)' и 'requireElement (Условие)'. 'get' означает, что он может возвращать значение null, но' require' означает, что он выдает исключение, если ничего не может быть возвращено. У меня есть проект https://github.com/stanleyxu2005/dutil/, вы можете проверить два класса проверки, и вы поймете. – stanleyxu2005

Смежные вопросы