76

Perl 社区的共识似乎是这Try::Tiny是处理异常的首选方式。

Perl 5.14(这是我使用的版本)似乎解决了该地址的问题。还会为我提供任何好处吗?evalTry::TinyTry::Tiny

4

6 回答 6

34

我的回答不受欢迎,但我认为 Perl 程序员不应该尝试使用我们在 Perl 中称为“异常”的东西的极其糟糕的概念。这些本质上是一个侧通道返回值。然而,仍然迷恋异常的想法,即使使用全局变量来传递状态的所有复杂性,人们仍然试图让它工作。

然而,实际上,人们习惯于die发出失败的信号。有人会说您可以die使用引用并传回错误对象,但您不需要这样做die。我们有对象,所以我们应该使用对象的所有力量:

 sub some_sub {
    ...
    return Result->new( error => 1, description => ... ) if $something_went_wrong;
    return Result->new( error => 0, ... );
    }

 my $result = some_sub( ... );
 if( $result->is_error ) { ... };

这不涉及全局变量、远距离操作、范围界定问题或需要特殊的特殊功能。你创建一个小类Result,或者任何你想调用它的东西,来包装你的返回值,这样你就有了结构化的数据,而不是没有身份的单个值。无需再好奇返回值的含义。这是undef真正的价值还是失败的迹象?如果它被定义或者它是真的返回值是好的?你的对象可以告诉你这些事情。而且,您可以将相同的对象与die. 如果您已经在使用该对象die并将其用作返回值,那么几乎没有什么可以推荐您必须做的所有额外的事情来容忍$@

我在“返回错误对象而不是抛出异常”中对此进行了更多讨论

但是,我知道你不能帮助别人做什么,所以你还是要假装 Perl 有例外。

于 2012-04-29T17:28:46.543 回答
31

这始终是个人喜好的情况。你比较喜欢哪个

my $rv;
if (!eval { $rv = f(); 1 } ) {
   ...
}

或者

my $rv = try {
   f();
} catch {
   ...
};

但请记住,后者使用 anon subs,所以它与return, 以及诸如此类next的东西混淆了。Try::Tiny 的 try-catch 很可能最终会变得更加复杂,因为您在 catch 块和它之外添加了通信通道。

返回异常的最佳情况(最简单)方案是$rv当没有异常时 if 始终为真。它如下所示:

my $rv;
if ($rv = eval { f() }) {
   ...
   return;
}

对比

my $rv = try {
   f();
} catch {
   ...
};

if (!$rv) {
   return;
}

这就是为什么我会使用TryCatch而不是Try::Tiny如果我使用这样的模块。

对 Perl 的改变仅仅意味着你可以再做if ($@)一次。换句话说,

my $rv;
if (!eval { $rv = f(); 1 } ) {
   ...
}

可以写

my $rv = eval { f() };
if ($@) {
   ...
}
于 2012-04-28T18:09:15.790 回答
14

如果不出意外,Try::Tiny仍然是不错的语法糖。如果你想要一些更重量级的东西,还有TryCatch,它解决了一些与子例程中的子句相关的问题Try::Tiny(例如,它return不离开封闭函数)。

于 2012-04-28T18:15:46.113 回答
11

Try::Tiny简单轻便。太容易了。我们遇到了两个问题:

  • 匿名return潜艇 - 里面的“ ”声明总是有问题
  • 总是抓住一切

所以我对 进行了一些更改Try::Tiny,这对我们有帮助。现在我们有:

try sub {},
catch 'SomeException' => sub {},
catch [qw/Exception1 Exception2/] => sub {},
catch_all sub {};

我知道 - 这种语法有点奇怪,但是由于明显的 ' sub',我们的程序员现在知道 ' return' 语句仅从异常处理程序中退出,并且我们总是只捕获我们想要捕获的异常 :)

于 2012-04-28T19:03:37.567 回答
1

Try::Tiny 很好,但在最后一个大括号上需要分号,并且不允许使用异常变量赋值,更不用说捕获异常类了。TryCatch过去做得很好,但已被新版本 0.006020Devel::Declare打破。另一个很棒的实现是Syntax::Keyword::Try,但它没有实现异常变量分配或捕获异常类。

有一个新模块Nice::Try,这是一个完美的替代品。

最后一个大括号不需要像 Try::Tiny 这样的分号。

您还可以进行异常变量赋值,例如

  try
  {
    # something
  }
  catch( $e )
  {
    # catch this in $e
  }

它也可以使用类异常,例如

  try
  {
    # something
  }
  catch( Exception $e )
  {
    # catch this in $e
  }

它还支持finally. 它的功能集使其非常独特。

完全披露:当 TryCatch 被破坏时,我开发了这个模块。

于 2020-06-02T05:04:29.457 回答
0

要么做:

local $@;
eval { … }

... 防止对 $@ 的更改影响全局范围,或使用 Try::Tiny。

从语法上讲,在某些情况下我更喜欢其中一种。

于 2016-08-22T15:37:53.627 回答