Perl 社区的共识似乎是这Try::Tiny
是处理异常的首选方式。
Perl 5.14(这是我使用的版本)似乎解决了该地址的问题。还会为我提供任何好处吗?eval
Try::Tiny
Try::Tiny
我的回答不受欢迎,但我认为 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 有例外。
这始终是个人喜好的情况。你比较喜欢哪个
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 ($@) {
...
}
如果不出意外,Try::Tiny
仍然是不错的语法糖。如果你想要一些更重量级的东西,还有TryCatch
,它解决了一些与子例程中的子句相关的问题Try::Tiny
(例如,它return
不离开封闭函数)。
Try::Tiny
简单轻便。太容易了。我们遇到了两个问题:
return
潜艇 - 里面的“ ”声明总是有问题所以我对 进行了一些更改Try::Tiny
,这对我们有帮助。现在我们有:
try sub {},
catch 'SomeException' => sub {},
catch [qw/Exception1 Exception2/] => sub {},
catch_all sub {};
我知道 - 这种语法有点奇怪,但是由于明显的 ' sub
',我们的程序员现在知道 ' return
' 语句仅从异常处理程序中退出,并且我们总是只捕获我们想要捕获的异常 :)
Try::Tiny 很好,但在最后一个大括号上需要分号,并且不允许使用异常变量赋值,更不用说捕获异常类了。TryCatch过去做得很好,但已被新版本 0.006020的Devel::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 被破坏时,我开发了这个模块。
要么做:
local $@;
eval { … }
... 防止对 $@ 的更改影响全局范围,或使用 Try::Tiny。
从语法上讲,在某些情况下我更喜欢其中一种。