我有一个从字典中弹出元素的函数,如果出现任何问题,应该抛出异常。代码看起来很不错。
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 :(得分:2)
David 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 :(得分: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
的顺序。并且编译器不会发出警告。
也许这种解决方法不适合你,但你必须提出这种性质的东西。
底线是您的分析正确,编译器错误。