任何人都可以提供一个代码示例,如何在类内部的变量更改上设置观察者?我尝试使用不同的功能( Scalar::Watcher 、 trigger attribute of Moo )和 OOP 框架(Moo、Mojo::Base)以多种方式实现此目的,但都失败了。
下面是我失败的代码,以便更好地理解我的任务。在此示例中,每次 attr1 发生更改时,我都需要更新 attr2。
使用 Mojo::Base 和 Scalar::Watcher:
package Cat;
use Mojo::Base -base;
use Scalar::Watcher qw(when_modified);
use feature 'say';
has 'attr1' => 1;
has 'attr2' => 2;
has 'test' => sub { # "fake" attribute for getting access to $self
my $self = shift;
when_modified $self->attr1, sub { $self->attr2(3); say "meow" };
};
package main;
use Data::Dumper;
my $me = Cat->new;
$me->attr1;
warn Dumper $me;
say $me->attr1(3)->attr2; # attr2 is still 2, but must be 3
使用 Moo 和触发器:
package Cat;
use Moo;
use Scalar::Watcher qw(when_modified);
use feature 'say';
has 'attr1' => ( is => 'rw', default => 1, trigger => &update() );
has 'attr2' => ( is => 'rw', default => 1);
sub update {
my $self = shift;
when_modified $self->attr1, sub { $self->attr2(3); say "meow" }; # got error here: Can't call method "attr1" on an undefined value
};
package main;
use Data::Dumper;
my $me = Cat->new;
$me->attr1;
warn Dumper $me;
say $me->attr1(3)->attr2;
非常感谢任何建议。
最佳答案
Moo 部分
got error here: Can't call method "attr1" on an undefined value
这是因为 Moo 期望将代码引用作为 has
的触发器
。您正在将调用结果传递给update
。这里的 &
并不给你一个引用,而是告诉 Perl 忽略 update
函数的原型(prototype)。你不想要这样。
相反,请使用 \&foo
创建引用,并且不要添加括号 ()
。您不想调用该函数,而是引用它。
has 'attr1' => ( is => 'rw', default => 1, trigger => \&update );
现在,完成此操作后,您不再需要 Scalar::Watcher。触发器已经做到了这一点。每次 attr1
发生更改时都会调用它。
sub update {
my $self = shift;
$self->attr2(3);
say "meow";
};
如果你现在运行整个程序,它会工作一点,但会因以下错误而崩溃:
Can't locate object method "attr2" via package "3" (perhaps you forgot to load "3"?) at
这是因为 attr1
返回新值,而不是对 $self
的引用。所有 Moo/Moose 访问器都是这样工作的。并且 3
不是一个对象,因此它没有方法 attr2
# this returns 1
# |
# V
say $me->attr1(3)->attr2;
相反,请通过两次调用来执行此操作。
$me->attr1(3);
say $me->attr2;
这是一个完整的示例。
package Cat;
use Moo;
use feature 'say';
has 'attr1' => ( is => 'rw', default => 1, trigger => \&update );
has 'attr2' => ( is => 'rw', default => 1 );
sub update {
my $self = shift;
$self->attr2(3);
say "meow";
}
package main;
my $me = Cat->new;
say $me->attr2;
$me->attr1(3);
say $me->attr2;
输出:
1
meow
3
为什么 Scalar::Watcher 不能与 Mojo 一起使用
首先,Mojo::Base没有提供触发机制。但是您实现 Scalar::Watcher 的方式无法工作,因为 test
方法从未被调用。我尝试在基于 Mojo::Base 的类中 Hook new
,以便在始终被调用的地方执行 when_modified
调用。
这里的一切都只是猜测。
下面的代码片段是我尝试过的,但它不起作用。我将在下面进一步解释原因。
package Cat;
use Mojo::Base -base;
use Scalar::Watcher qw(when_modified);
use feature 'say';
has 'attr1' => '1';
has 'attr2' => 'original';
sub new {
my $class = shift;
my $self = $class->SUPER::new(@_);
when_modified $self->{attr1}, sub { $self->attr2('updated'); say "meow" };
return $self;
}
如您所见,这现在是 new
调用的一部分。代码确实被执行了。但这没有帮助。
documentation of Scalar::Watcher states观察者应该在那里直到变量超出范围。
If when_modified is invoked at void context, the watcher will be active until the end of $variable's life; otherwise, it'll return a reference to a canceller, to cancel this watcher when the canceller is garbage collected.
但我们实际上没有标量变量。如果我们尝试这样做
when_modified $self->foo
然后 Perl 对 $self
执行 foo
方法调用,when_modified
将获取该调用的返回值。我还尝试深入到上面对象的内部,但这也不起作用。
我的 XS 不够强大,无法理解发生了什么 here ,但我认为它在附加这种魔法时遇到了一些麻烦。它不能与哈希引用值一起使用。也许这就是它被称为 Scalar::Watch 的原因。
关于perl - 观察 Perl 类中属性的变化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43326596/