我需要能够将动作添加到动作可能会消失的词汇块的末尾。而且我需要正常抛出异常并能够正常捕获。
不幸的是,在 DESTROY 期间 Perl 的特殊情况异常通过在消息中添加“(in cleanup)”并使它们无法捕获。例如:
{
package Guard;
use strict;
use warnings;
sub new {
my $class = shift;
my $code = shift;
return bless $code, $class;
}
sub DESTROY {
my $self = shift;
$self->();
}
}
use Test::More tests => 2;
my $guard_triggered = 0;
ok !eval {
my $guard = Guard->new(
#line 24
sub {
$guard_triggered++;
die "En guarde!"
}
);
1;
}, "the guard died";
is $@, "En guarde! at $@ line 24\n", "with the right error message";
is $guard_triggered, 1, "the guard worked";
我希望它通过。目前,异常完全被 eval 吞没了。
这是为 Test::Builder2 准备的,所以我只能使用纯 Perl。
根本问题是我有这样的代码:
{
$self->setup;
$user_code->();
$self->cleanup;
}
即使 $user_code 死了,也必须进行清理,否则 $self 会进入奇怪的状态。所以我这样做了:
{
$self->setup;
my $guard = Guard->new(sub { $self->cleanup });
$user_code->();
}
之所以如此复杂,是因为清理会运行任意用户代码,而这是一个代码将死掉的用例。我希望该异常是可捕获的并且不会被警卫改变。
由于改变堆栈的方式,我避免将所有内容包装在 eval 块中。