如果Perl代码出现问题,最佳做法是什么?

时间:2010-03-07 11:25:48

标签: perl exception-handling

  

可能重复:
  How can I cleanly handle error checking in Perl?
  What’s broken about exceptions in Perl?

我看到代码的效果如下:

do_something($param) || warn "something went wrong\n";

我也看到了这样的代码:

eval {
  do_something_else($param);
};
if($@) {
  warn "something went wrong\n";
}

我应该在所有子程序中使用eval / die吗?我应该根据子程序返回的内容编写所有代码吗?不是eval代码(一遍又一遍)会让我慢下来吗?

6 个答案:

答案 0 :(得分:13)

阻止eval不是字符串eval,所以不,它不慢。绝对推荐使用它。

它的工作方式有一些令人烦恼的微妙之处(大多数令人讨厌的$@是一个全局变量的副作用),所以考虑使用Try::Tiny而不是记住所有的你需要在防御性地使用eval的小技巧。

答案 1 :(得分:6)

do_something($param) || warn "something went wrong\n";

在这种情况下,如果出现问题,do_something应返回错误代码。要么它不能死,要么它死了,这是一个非常不寻常的情况。

eval {
  do_something_else($param);
};
if($@) {
  warn "something went wrong\n";
}

这里假设do_something_else通过抛出异常来解决出错的唯一机制。

如果do_something_else在真正异常情况下抛出异常并在其他情况下返回错误值,则还应检查其返回值。

使用eval的块形式不会在运行时导致额外的编译,因此没有严重的性能缺陷:

  

在第二种形式中,BLOCK中的代码只被解析一次 - 同时解析eval本身周围的代码 - 并在当前Perl程序的上下文中执行。此表单通常用于比第一个表单更有效地捕获异常(见下文),同时还提供了在编译时检查BLOCK内的代码的好处。

答案 2 :(得分:6)

warn非常讨厌的模块。成功或失败。不要在终端上打印东西然后继续运行;我的程序无法根据您打印的某些消息采取措施。如果程序可以继续运行,只有在明确告知它没有问题时才打印一条消息。如果程序无法继续运行,die。这就是它的用途。

当出现问题时,始终抛出异常。如果您可以解决问题,请修复它。如果你无法解决问题,请不要尝试;抛出异常并让调用者处理它。 (如果你不能处理你呼叫的异常,请不要。)

基本上,许多程序出错的原因是因为他们试图修复他们无法解决的错误。在问题的第一个迹象下干净地死亡的程序易于调试和修复。一个在困惑时继续运行的程序只会破坏数据并使每个人烦恼。所以不要这样做。尽快死去。

答案 3 :(得分:5)

你的两个例子完全不同。第一个检查错误的返回值,并采取一些响应操作。第二个检查被叫代码的实际死亡。

您必须自己决定哪种行为适合每种情况。我建议在大多数情况下简单地返回false。如果您遇到严重到无法继续的错误(或者没有任何意义继续下去,那么您应该只明确die。但即便如此,您仍然可以返回错误。

eval {}中包含块与在eval ""中包装任意代码不同。在前一种情况下,代码仍然在编译时解析,并且您不会产生任何额外的开销。您将简单地捕获该代码的任何死亡(但除了在$@中留给您的值之外,您不会有任何关于出现问题或代码有多远的迹象)。在后一种情况下,Perl解释器将代码视为一个简单的字符串,直到它被实际评估为止,因此在调用解释器时会有一定的成本(并且您将失去对代码的所有编译时检查)。 / p>

顺便提一下,您调用eval并检查$@值的方式不是推荐形式;有关Perl中异常陷阱和技术的广泛讨论,请参阅this discussion

答案 4 :(得分:2)

第一个版本非常“perlish”,非常简单易懂。这个成语的唯一缺点是它只对短的情况可读。如果错误处理需要更多逻辑,请使用第二个版本。

答案 5 :(得分:2)

没有人真正解决过这个问题的“最佳实践”部分,所以我会跳进去。

是的,你应该肯定在出现问题时在你的代码中抛出一个异常,你应该尽早这样做(所以你要限制需要调试的代码来解决什么是导致它。)

执行返回undef以表示失败的代码并不是特别可靠,因为人们倾向于使用它而不检查undef returnvalue - 这意味着他们假设的变量实际上有一些有意义的东西不得。这导致了复杂,难以调试的问题,甚至在以前工作的代码中出现意外问题。

更加可靠的方法是编写代码,以便在出现问题时将其终止,然后只有在需要从该故障中恢复时,才能在{{1>中包含对它的任何调用(或者,更好,来自Try::Tinyeval{ .. },如上所述)。在大多数情况下,调用代码无法进行任何有意义的恢复,因此在常见情况下调用代码仍然很简单,您可以假设您将获得有用的值。如果出现问题,那么您将从失败的代码的实际部分收到错误消息,而不是默默地获取undef。如果您的调用代码可以做一些事情来恢复失败,那么它可以安排捕获异常并做任何需要的事情。

值得一读的东西是Exception classes,它是一种将额外信息发送到调用代码的结构化方法,并允许它选择要捕获的异常以及无法处理的异常。您可能不希望在代码中的任何地方使用它们,但是如果您有一些复杂的东西可能会以同样复杂的方式失败,并且您希望安排失败可以恢复,那么它们是一种有用的技术。