fri*_*edo 29 syntax perl exception-handling exception
另一个问题的讨论让我想知道:Perl缺少其他编程语言的异常系统是什么?
Perl的内置异常有点特别,因为它们像Perl 5对象系统一样,在事后的想法中加以分类,并且它们超出了其他不专门用于异常的关键字(eval和die).
与具有内置try/throw/catch类型语法的语言相比,语法可能有点难看.我通常这样做:
eval {
do_something_that_might_barf();
};
if ( my $err = $@ ) {
# handle $err here
}
Run Code Online (Sandbox Code Playgroud)
有几个CPAN模块提供语法糖来添加try/catch关键字,并允许轻松声明异常类层次结构等等.
我在Perl的异常系统中看到的主要问题是使用特殊的全局$@来保存当前错误,而不是catch从范围的角度来看可能更安全的专用类型机制,尽管我从来没有亲自遇到任何问题$@得到了.
jro*_*way 25
Try :: Tiny(或构建在它之上的模块)是处理Perl 5中异常的唯一正确方法.涉及的问题很微妙,但链接的文章详细解释了它们.
以下是如何使用它:
use Try::Tiny;
try {
my $code = 'goes here';
succeed() or die 'with an error';
}
catch {
say "OH NOES, YOUR PROGRAM HAZ ERROR: $_";
};
Run Code Online (Sandbox Code Playgroud)
eval并且$@是您不需要关心的移动部件.
有些人认为这是一个kludge,但在阅读了其他语言(以及Perl 5)的实现后,它与其他语言没有什么不同.只有$@移动部件,你可以让你的手被抓住...但与其他具有暴露的移动部件的机器一样......如果你不触摸它,它不会从你的手指上撕下来.所以使用Try :: Tiny并保持打字速度;)
dao*_*oad 24
大多数人学会处理异常的典型方法很容易丢失被困异常:
eval { some code here };
if( $@ ) { handle exception here };
Run Code Online (Sandbox Code Playgroud)
你可以做:
eval { some code here; 1 } or do { handle exception here };
Run Code Online (Sandbox Code Playgroud)
这可以避免由于$@被破坏而错过异常,但它仍然容易失去价值$@.
为了确保你没有破坏异常,当你做eval时,你必须本地化$@;
eval { local $@; some code here; 1 } or do { handle exception here };
Run Code Online (Sandbox Code Playgroud)
这都是微妙的破损,预防需要很多深奥的样板.
在大多数情况下,这不是问题.但是我被实际代码中的异常吃掉对象析构函数所灼伤.调试问题很糟糕.
情况显然很糟糕.看看CPAN上构建的所有模块都提供了不错的异常处理.
对Try :: Tiny赞成的压倒性反应加上Try :: Tiny不是"太聪明一半"的事实,已经说服我尝试一下.喜欢的东西TryCatch和异常::类:: TryCatch, 错误,和和太复杂,我相信.Try :: Tiny是朝着正确方向迈出的一步,但我仍然没有使用轻量级异常类.
Eth*_*her 13
某些异常类(例如Error)无法处理try/catch块中的流控制.这会导致细微的错误:
use strict; use warnings;
use Error qw(:try);
foreach my $blah (@somelist)
{
try
{
somemethod($blah);
}
catch Error with
{
my $exception = shift;
warn "error while processing $blah: " . $exception->stacktrace();
next; # bzzt, this will not do what you want it to!!!
};
# do more stuff...
}
Run Code Online (Sandbox Code Playgroud)
解决方法是使用状态变量并检查try/catch块之外的内容,这对我看起来非常像臭臭的n00b代码.
错误中的另外两个"陷阱"(这两个都让我感到悲伤,因为如果你之前没有遇到过这个问题,它们很难调试):
use strict; use warnings;
try
{
# do something
}
catch Error with
{
# handle the exception
}
Run Code Online (Sandbox Code Playgroud)
看起来很明智,对吗?此代码编译,但会导致奇怪和不可预测的错误.问题是:
use Error qw(:try)被省略,因此try {}...块将被错误地分配(您可能会看到或不会看到警告,具体取决于您的其余代码)try是原型方法调用.噢,这也提醒了我,因为try,catch等都是方法调用,这意味着那些块中的调用堆栈不会是你所期望的.(实际上有两个额外的堆栈级别,因为Error.pm内部有一个内部调用.)因此,我有一些模块充满了这样的样板代码,这只会增加混乱:
my $errorString;
try
{
$x->do_something();
if ($x->failure())
{
$errorString = 'some diagnostic string';
return; # break out of try block
}
do_more_stuff();
}
catch Error with
{
my $exception = shift;
$errorString = $exception->text();
}
finally
{
local $Carp::CarpLevel += 2;
croak "Could not perform action blah on " . $x->name() . ": " . $errorString if $errorString;
};
Run Code Online (Sandbox Code Playgroud)
我最近遇到的eval异常机制问题与$SIG{__DIE__}处理程序有关.我错了 - 假设这个处理程序只在Perl解释器退出时被调用,die()并且想要使用这个处理程序来记录致命事件.然后我发现我在库代码中记录异常是致命错误,这显然是错误的.
解决方案是检查$^S或$EXCEPTIONS_BEING_CAUGHT变量的状态:
use English;
$SIG{__DIE__} = sub {
if (!$EXCEPTION_BEING_CAUGHT) {
# fatal logging code here
}
};
Run Code Online (Sandbox Code Playgroud)
我在这里看到的问题是__DIE__处理程序在两个相似但不同的情况下使用.这个$^S变量看起来像是我的后期附加组件.不过,我不知道情况是否真的如此.