10

处理 Perl 方法链中抛出的异常的最佳方法是什么?如果任何链接的方法抛出异常,我想分配一个值 0 或 undef

代码示例:

my $x = $obj->get_obj->get_other_obj->get_another_obj->do_something;

最好的方法是什么?我是否必须每次都包含在 try/catch/finally 语句中?我想应用的上下文是:我在使用 Catalyst 和 DBIC 进行 Web 开发工作,我做了很多链接的结果集,如果其中一些结果集抛出异常,我只想分配一个 0 或 undef 的值,然后处理这个模板中的错误(我正在使用模板工具包)。如果有另一种方法可以做到这一点,而无需将每个调用都包含在 try/catch 中,请告诉我。如果您知道在相同上下文(Catalyst/DBIC/TT)中处理此类错误的更好方法,请提出建议。一个实际的例子是当用户搜索某些东西并且它不存在时。

4

3 回答 3

9

我通过在故障点返回一个空对象来处理这个问题。该对象通过简单地返回自身来响应每个方法,因此它会一直这样做,直到它吃完剩余的方法。最后,您查看$x它是否是您预期的结果或这个空对象。

这是这样的一个例子:

use v5.12;

package Null {
    my $null = bless {}, __PACKAGE__;
    sub DESTROY { 1 }
    sub AUTOLOAD { $null }
    }

对于每个调用的方法,AUTOLOAD 都会拦截它并返回空对象。

当您遇到错误时,您将返回这些 Null 对象之一。在方法链的中间,您仍然可以返回一个对象,因此当您调用下一个方法时 Perl 不会崩溃。

sub get_other_obj {
    ...;
    return Null->new if $error;
    ...;
    }

在链的末尾,您可以检查返回的内容是否为 Null 对象。如果这就是你得到的,那么糟糕的事情发生了。

这是基本的想法。您可以改进 Null 类以使其记住消息及其创建位置,或者添加一些多态方法(例如sub is_success { 0 })以使其与您期望获得的对象的接口很好地配合使用。

我以为我在某个地方写了很长的关于这个的东西,但现在我找不到了。

更新:发现了其中一些著作:

于 2011-08-15T17:23:19.967 回答
3

您可以编写一个标量方法,该方法将在错误处理中包装方法链:

my $try = sub {
    @_ > 1 or return bless {ok => $_[0]} => 'Try';

    my ($self, $method) = splice @_, 0, 2;
    my $ret;
    eval {
        $ret = $self->$method(@_);
    1} or return bless {error => $@} => 'Try';
    bless {ok => $ret} => 'Try'
};

{package Try;
    use overload fallback => 1, '""' => sub {$_[0]{ok}};
    sub AUTOLOAD {
        my ($method) = our $AUTOLOAD =~ /([^:]+)$/;
        $_[0]{ok} ? $_[0]{ok}->$try($method, @_[1..$#_]) : $_[0]
    }
    sub DESTROY {}
    sub error {$_[0]{error}}
}

使用它:

{package Obj;
    sub new {bless [0]}
    sub set {$_[0][0] = $_[1]; $_[0]}
    sub add {$_[0][0] += ($_[1] || 1); $_[0]}
    sub show {print "Obj: $_[0][0]\n"}
    sub dies  {die "an error occured"}
}

my $obj = Obj->new;

say "ok 1" if $obj->$try(set => 5)->add->add->show; # prints "Obj 7"
                                                    # and "ok 1"

say "ok 2" if $obj->$try('dies')->add->add->show;   # prints nothing 

say $obj->$try('dies')->add->add->show->error;  # prints "an error occured..."

$try方法的第一行还允许使用以下语法:

say "ok 3" if $obj->$try->set(5)->add->add->show;
于 2011-08-15T17:58:25.440 回答
-1

一个想法是创建一个类,overload当在字符串/数字/布尔上下文中评估实例对象时,该类用于返回错误值,但仍允许对其调用方法。方法总是可以AUTOLOAD返回$self,允许方法链传播相同的错误。

于 2011-08-15T17:24:15.337 回答