当函数引发异常时,TStrings会返回"无法访问的值"。

5

有人能帮我解释一下为什么在底层函数抛出异常时,TStrings被返回为“无法访问的值”吗?

function GetStrings():TStrings;
begin
  result := TStringList.Create;
  try
    raise exception.Create('Error Message');
  except
    FreeAndNil(result);
    raise
  end;
end;

procedure TFormMain.Button1Click(Sender: TObject);
var
  S : TStrings;
begin
  try
    S := GetStrings;
    try
      //Do awesome stuff
    finally
      FreeAndNil(S)
    end;
  except
    //Debug watch: S = "Inaccessible value"
    Assert(S = nil, 'Assertion failure, S should be nil'); 
  end;
end;

如果我在重新引发异常之前不调用FreeAndNil(result);,函数GetStrings也会返回 "无法访问的值"。但是这样一来,我就有了一个内存泄漏问题:-O


1
+1 很好的问题,提问得很好。 - David Heffernan
1个回答

6
try
  S := GetStrings; 
  try
    //Do awesome stuff
  finally
    FreeAndNil(S)
  end;
except
  //Debug watch: S = "Inaccessible value"
  Assert(S = nil, 'Assertion failure, S should be nil'); 
end;

以下是执行顺序:
  1. 由于S是一个本地变量,因此在初始化之前其值是不确定的。这是代码开始执行时的状态。
  2. 调用GetStrings函数。
  3. GetStrings中引发了异常。这意味着GetStrings没有将执行返回给调用者,并且导致S没有被赋值。
  4. 由于try块未执行,因此finally块不会执行。
  5. 接下来,执行except块,S具有不确定的值,这就是您观察到的情况。
实际上,编译器应该警告(W1036)在except块中引用S时其可能尚未初始化。但它似乎无法做到这一点,这相当可悲。
如果您希望在except块中引用S,则需要在try/finally中嵌套它。
S := GetStrings; 
try
  try
    //Do awesome stuff
  except
    //Do stuff with S
    Assert(S <> nil);
  end;
finally
  FreeAndNil(S)
end;

另一种思考方式是,S 只有在赋值给它的代码和销毁对象的代码之间才有意义。因此,如果 except 块需要引用对象,则 try/except 必须嵌套在 try/finally 中。


2
你能将S:= nil; try S := GetStrings;翻译成中文吗? - Passella
1
@Passella你可以这么做,但我不会这样做。 - David Heffernan
1
最近我在XE5中调试一些代码时发现,尽管我在函数中为Result分配了一个有效值,但随后引发的异常似乎阻止了该值被返回,因为从函数返回的结果是随机垃圾(不,不是引起异常的对象)。在这种情况下,我期望GetStrings()返回NIL,而不是指向TStringList的有效指针,仅仅因为except子句在函数退出之前调用了FreeAndNil(Result)。 - David Schwartz
1
@DavidSchwartz,您的理解有误。当函数抛出异常时,不会返回任何值。当您编写 x = foo(); 并且 foo() 抛出异常时,则不会将任何值分配给 x - David Heffernan
3
@DavidSchwartz,在函数中,Result 是一个本地变量。一般情况下,它不是在返回时分配给函数结果的变量的别名。(这样的变量不一定存在!)在 GetStrings 中赋值给 Result 并不会立即修改调用者中的 S。相反,在 GetStrings 返回后才会对 S 进行赋值。如果 GetStrings 抛出异常,则它不会返回,因此在这种情况下 S 不会被赋值。 - Rob Kennedy
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接