任何人都可以提供一个代码示例,如何在类中设置变量更改的观察者?我尝试使用不同的功能(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;
非常感谢任何建议。
答案 0 :(得分:5)
此处出现错误:无法调用方法" attr1"在未定义的值
这是因为Moo希望代码引用为trigger
的{{1}}。您正在将呼叫结果传递给has
。 update
这里没有给你一个引用,而是告诉Perl忽略&
函数的原型。你不想要那个。
相反,请使用update
创建引用,并且不要添加括号\&foo
。您不想调用该功能,您想引用它。
()
现在,一旦你完成了这项工作,你就不再需要Scalar :: Watcher了。触发器已经这样做了。每次has 'attr1' => ( is => 'rw', default => 1, trigger => \&update );
更改时都会调用它。
attr1
如果你现在运行整个事情,它会工作一点,但崩溃时会出现这个错误:
找不到对象方法" attr2"通过包" 3" (也许你忘了加载" 3"?)
这是因为sub update {
my $self = shift;
$self->attr2(3);
say "meow";
};
会返回新值,而不会引用attr1
。所有Moo / Moose存取器都是这样工作的。 $self
不是对象,因此它没有方法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;
首先,Mojo :: Base不提供触发机制。但是你实现Scalar :: Watcher的方式无法工作,因为从未调用过1
meow
3
方法。我尝试在基于Mojo :: Base的类中test
挂钩,在一个始终被调用的地方进行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;
}
电话的一部分。代码确实被执行了。但它没有帮助。
观察者应该在那里的documentation of Scalar::Watcher states,直到变量超出范围。
如果在void上下文中调用when_modified,则观察者将是 活跃到$ variable的生命结束;否则,它会返回一个 引用取消器,取消该取消器时取消此观察者 垃圾收集。
但我们实际上并没有标量变量。如果我们尝试做
new
然后Perl在when_modified $self->foo
上对foo
进行方法调用,$self
将获得该调用的返回值。我也尝试进入上述对象的内部,但这也没有用。
我的XS不够强大,无法理解here发生了什么,但我认为在加入魔法时遇到了一些麻烦。它不能使用散列引用值。可能这就是为什么它被称为Scalar :: Watch。