在Perl中,处理异常的最佳方式是什么?

31
我注意到Perl社区似乎没有广泛使用Exception.pm和Error.pm。这是由于异常处理中eval的大量占用吗?
此外,总体上看,Perl程序似乎对异常处理有更宽松的政策。这是否有一个强有力的原因呢?
无论如何,在Perl中处理异常的最佳方法是什么?

2
重复的https://dev59.com/93RB5IYBdhLWcg3wz6Wd https://dev59.com/3HI95IYBdhLWcg3wxA09 https://dev59.com/RHE95IYBdhLWcg3wI6ft https://dev59.com/HEnSa4cB1Zd3GeqPNFzb -- 我们真的需要另一篇关于Perl异常处理的文章吗? - Ether
3个回答

58
Perl社区的共识似乎是Try::Tiny是处理异常的首选方式。你提到的“宽容策略”可能是由以下因素的组合造成的:
  • Perl不是完全面向对象的语言。(例如,与Java相比,你无法避免处理异常。)
  • 许多Perl开发人员的背景。(像C和shell这样的语言没有异常机制。)
  • 人们倾向于使用Perl进行的任务类型。(用于文本处理和报告生成的小脚本不需要异常处理。)
  • Perl没有(好的)内置异常机制。

请注意,最后一项意味着你会看到很多这样的代码:

eval { something() };
if ($@) {
    warn "Oh no! [$@]\n";
}

即使它没有使用try/catch语法,这也是异常处理。然而,它很脆弱,在许多微妙的边缘情况下会出现问题,大多数人不会考虑到。Try::Tiny和CPAN上的其他异常处理模块是为了更容易地正确处理它们而编写的。
1. C确实有setjmp()和longjmp(),可用于非常粗糙的异常处理形式。

16
为了澄清一个常见的误解,也许原帖的作者已经理解了,也许没有:eval BLOCK不是eval STRING,它不会在运行时编译代码。它只是一种异常处理方法——带有一个有趣的名称和稍微有点奇怪语义的try方法。 - hobbs
1
如果我们将setjmp+longjmp视为C语言中的异常处理机制(尽管粗糙),那么shell也有一个:trap+kill(尽管看起来更加粗糙)。 - Ruslan
1
提供信息,现在Perl中有一个完整的try-catch实现,使用模块Nice::Try - Jacques

8

请不要直接测试 $@ 变量,因为它是一个全局变量,即使测试本身也可能会改变它。

通用的 eval-template:

my $result;

eval {
    $result= something();
    # ...
    1;  # ok
} or do {
    my $eval_error= $@ || "error";
    # ...
    die $eval_error;
};  # needs a semicolon

实际上这是最轻量级的方式。这种方法还是会留下一点点有趣的$@行为的空间,但是并没有引起我太多的关注。

2
正如所提到的,你可以使用传统的方式 eval,但如果你想使用更复杂的异常捕获方式,包括使用异常对象,则建议使用 try-catch-finally 块。 有许多 Perl 模块可供选择,例如Nice::TrySyntax::Keyword::Try,但Syntax::Keyword::Try不提供异常变量赋值或异常类捕获。
  try
  {
    # something
  }
  catch( Exception $e )
  {
    # catch this in $e
  }

完整披露:我是Nice::Try的开发者。

1
Nice::Try非常好,它拥有我一直想要的处理Perl异常的所有功能。 - Dave Baird

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