如何处理 Perl 方法链中的错误?

Posted

技术标签:

【中文标题】如何处理 Perl 方法链中的错误?【英文标题】:How do I handle errors in methods chains in Perl? 【发布时间】:2011-10-27 06:09:40 【问题描述】:

在 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)中处理此类错误的更好方法,请提出建议。 一个实际的例子是当用户搜索某些东西但它不存在时。

【问题讨论】:

【参考方案1】:

我通过在故障点返回一个空对象来处理这个问题。该对象通过简单地返回自身来响应每个方法,因此它会一直这样做,直到它吃完剩余的方法。最后,您查看$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 )以使其与您期望获得的对象的接口很好地配合。

我以为我在某处写了很长的东西,但现在我找不到了。

更新:找到了其中的一些著作:

The Null Mull No ifs, ands, or buts

【讨论】:

这里有一个问题:Setter 方法将使用一个空条目来表示返回当前值。例如,$foo->Name("David") will set the name to David, and $foo->Name` 将返回当前名称。因此,一个方法的 null 返回可能是另一个方法的有效输入。 这不是问题。这是一个无操作。随后的方法不做任何事情。您没有将 null 对象作为参数传递;它是参照物。如果前一个方法没有返回对象,则无论如何都不能链接。 对不起,我没听懂。如何在 DBIC 链接结果集中应用它? 我知道这是旧的,但它看起来像什么?这个线程(perlmonks.org/?node_id=899958)似乎表明 Moose/Mouse 人不喜欢返回 undef(默认情况下 perlcritic 也抱怨它),特别注意 stvn 的 cmets。还有,Perl 中的“空对象”是什么?您是在祝福包含 null 的 hashref 还是只是“返回 undef”? 他们在谈论一个明确的return undef,它在列表上下文中存在问题,因为它是一个项目的列表。我说的是返回一个对象,而其他一切都期望一个对象。【参考方案2】:

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

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;

【讨论】:

【参考方案3】:

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

【讨论】:

以上是关于如何处理 Perl 方法链中的错误?的主要内容,如果未能解决你的问题,请参考以下文章

如何处理c#中的错误代码

在 Sequelize 中使用 .create(...) 方法时如何处理错误

如何处理 VBA 中的 DLL 错误?

如何处理 NSPersistentContainer.loadPersistentStores 中的错误?

如何处理完成闭包中的多个错误

在 Perl 中,我如何处理整个哈希?