Perl中的异常有什么问题?

fri*_*edo 29 syntax perl exception-handling exception

另一个问题的讨论让我想知道:Perl缺少其他编程语言的异常系统是什么?

Perl的内置异常有点特别,因为它们像Perl 5对象系统一样,在事后的想法中加以分类,并且它们超出了其他不专门用于异常的关键字(evaldie).

与具有内置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并保持打字速度;)

  • 请注意,Perl> = 5.14的版本现在尝试保持异常正常,因此这不再是"唯一正确的方法".但它仍然相当不错. (4认同)

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)

看起来很明智,对吗?此代码编译,但会导致奇怪和不可预测的错误.问题是:

  1. use Error qw(:try)被省略,因此try {}...块将被错误地分配(您可能会看到或不会看到警告,具体取决于您的其余代码)
  2. 捕获块后缺少分号!非直观的控制块不使用分号,但实际上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)

  • 对于downvoter:我会欢迎改进这个丑陋代码的建议,它也会让我感到不快.我已经在后续项目中切换到TryCatch,但这篇文章旨在展示特定异常类的缺陷(它可能扩展到其他人;我还没有尝试过所有这些). (3认同)

hil*_*llu 9

我最近遇到的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变量看起来像是我的后期附加组件.不过,我不知道情况是否真的如此.