为什么在明显"安全"的代码中使用异常处理?

lyb*_*rko 6 delphi try-except

请问,有人可以解释一下,在这段代码中会出现什么异常?

function CreateBibleNames: TStrings;
begin
  Result := TStringList.Create;
  try
    Result.Add('Adam');
    Result.Add('Eva');
    Result.Add('Kain');
    Result.Add('Abel');
  except
    Result.Free;
    raise;
  end;      
end;
Run Code Online (Sandbox Code Playgroud)

因为我使用delphi,所以我曾经使用过一次异常处理.我认为上面的代码是由熟练的程序员编写的,我不认为异常是多余的.但是,在这个概念中使用异常处理对我来说仍然是一个谜.它似乎是一个安全的代码(没有尝试除了结束).我已经多次看到类似这样的代码片段,这就是为什么尽管有我的经验,这可能是一个很好的理由以这种方式编写,但这并不是必要的.

此外,当事情失败时,我会得到异常描述......

感谢名单

Nic*_*ges 12

好吧,那段代码很奇怪,我同意,而且我完全理解为什么它这样编写.但它是这样编写的,因为代码背后的前提是错误的.构造看起来很奇怪的事实应该是"代码味道",并且应该告诉你可能没有尽可能以最好的方式完成某些事情.

首先,这就是为什么try ...中的异常构造除了块.该函数创建一个TStringList,但如果在填充过程中出现问题,那么创建的TStringList将在堆上"丢失"并将成为内存泄漏.因此,原始程序员是防御性的,并确保如果发生异常,将释放TStringList,然后再次引发异常.

现在这里是"坏前提"部分.该函数返回一个TStrings实例.这并不总是最好的方法.返回像这样的对象的实例会引出一个问题"谁将要处理我创建的这个东西?".它创建了一种情况,在调用方面可能很容易忘记已经分配了TStrings实例.

这里的"更好的实践"是让函数将TStrings作为参数,然后填写现有实例.这样,毫无疑问谁拥有该实例(调用者),从而管理其生命周期.

因此,该函数成为一个过程,可能如下所示:

procedure CreateBibleNames(aStrings: TStrings);
begin
  if aStrings <> nil then
  begin
    aStrings .Add('Adam');
    aStrings .Add('Eva');
    aStrings .Add('Kain');
    aStrings .Add('Abel');
  end;      
end;
Run Code Online (Sandbox Code Playgroud)

现在这并不是说每次返回一个对象实例都是一件坏事 - 例如,这是非常有用的Factory模式的唯一功能.但是对于像这样的更"一般"的功能,上面是一种"更好"的做事方式.

  • 每当有人创建对象实例时,他们都会调用函数来返回新实例.这个函数(通常称为Create或CreateXXX)是一种特殊的函数(构造函数); 但仍然是一个功能.因此,每当有人调用这样的Create函数时,他们就有责任管理资源(即在必要时销毁对象).提示:不要调用你的程序`程序**创建**BibleNames(aStrings:TStrings); `...因为那不是**它正在做什么. (11认同)

Bar*_*lly 9

从函数返回新构造的对象时,这是一个很好的习惯.在比字符串列表更复杂的场景中,某些不变量可能会被破坏或发生其他错误,因此会出现异常,在这种情况下,您希望释放返回值.在可能发生这种情况时,不必检查每种可能的情况,通常一个好的做法是使用单一模式返回新构造的对象并随处跟随它.

这样,当您构建的对象的实现在将来发生变化时,如果它们碰巧抛出异常,您仍然是安全的.

  • @Runner:存在异常的事实意味着结果是**NOT**返回.并且没有理由设置一个永远不会被看到的值(包括零). (3认同)
  • 但我仍然会打电话给FreeAndNil(结果).确实传递了异常,但在这种情况下函数返回nil更加安全.你永远不知道接下来会发生什么,并且nil表示结果无效. (2认同)

Lar*_*s D 5

我会这样编写代码,原因如下:

1)通过标准化内存分配的外观,可以轻松检查源代码以查看是否缺少某些内容,而无需读取所有行。在这里,您只看到 .Create,并注意到异常处理很好,因此您不必阅读 try... except 之间的部分。

2)通过标准化内存分配的编码方式,即使在必要时,您也永远不会忘记这样做。

3)如果您稍后更改源代码、插入一些代码、更改 .Add 的工作方式、用其他内容替换 TStringList 等,那么它不会破坏此代码。

4)这意味着你不必考虑TStringList.Add()是否会抛出异常,从而解放你的大脑去从事其他提供更多价值的工作。