perl - 观察 Perl 类中属性的变化

标签 perl oop watch mojolicious moo

任何人都可以提供一个代码示例,如何在类内部的变量更改上设置观察者?我尝试使用不同的功能( Scalar::Watchertrigger 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/

相关文章:

c++ - 从带有for循环的函数返回时检查对象是否存在

Python:通过变量名引用对象属性?

visual-studio - 当私有(private)成员变量更改值时,如何在 Visual Studio 调试器中停止执行?

ios - 如何在 KONY 应用程序中集成我的应用程序委托(delegate)和 View Controller 代码?

xml - 使用 Perl 的 XML 标签内的总和和平均值

mysql - 有没有办法通过解析文本文件来填写Sharepoint条目?

perl - 在 fork/execs 的程序上使用 Devel::NYTProf

perl - 在运行时从方法修饰符应用角色

javascript - 如何为这种类型的构造函数创建原型(prototype)?

javascript - 从 node.js 检测文件内容更改