18

如何在 Perl6 中编写自定义访问器方法?

如果我有这门课:

class Wizard {
    has Int $.mana is rw;
}

我可以做这个:

my Wizard $gandalf .= new;
$gandalf.mana = 150;

假设我想在我的 Perl6 类中的 setter 中添加一个小检查而不放弃$gandalf.mana = 150;符号(换句话说,我不想写这个:)$gandalf.setMana(150);。如果程序试图设置负法力,它应该死掉。我该怎么做呢?Perl6 文档只提到可以编写自定义访问器,但没有说明如何编写。

4

2 回答 2

19

在 Rakudo 的更新版本中,有一个名为的子集将UInt其限制为正值。

class Wizard {
  has UInt $.mana is rw;
}

这样,如果您需要这样的事情,您就不会陷入困境;这是定义的方式:(
您可以省略my,但我想向您展示Rakudo 源代码的实际行)

my subset UInt of Int where * >= 0;

你也可以这样做:

class Wizard {
  has Int $.mana is rw where * >= 0;
}

我想指出,* >= 0in约束只是创建Callablewhere的一种捷径。

您可以将以下任何一项作为where约束:

... where &subroutine # a subroutine that returns a true value for positive values
... where { $_ >= 0 }
... where -> $a { $a >= 0 }
... where { $^a >= 0 }
... where $_ >= 0 # statements also work ( 「$_」 is set to the value it's testing )

(如果您希望它不为零,您也可以使用... where &prefix:<?>它可能更好地拼写为... where ?*or ... where * !== 0


如果您觉得使用您的代码的人很烦人,您也可以这样做。

class Wizard {
  has UInt $.mana is rw where Bool.pick; # accepts changes randomly
}

如果您想在查看类中的所有值时确保该值“有意义”,那么您将不得不做更多的工作。
(它可能还需要更多的实现知识)

class Wizard {
  has Int $.mana; # use . instead of ! for better `.perl` representation

  # overwrite the method the attribute declaration added
  method mana () is rw {
    Proxy.new(
      FETCH => -> $ { $!mana },
      STORE => -> $, Int $new {
        die 'invalid mana' unless $new >= 0; # placeholder for a better error
        $!mana = $new
      }
    )
  }
}
于 2015-07-28T18:13:35.470 回答
15

$.mana您可以通过声明方法获得与说提供的相同的访问器接口is rw。然后你可以像这样围绕底层属性包装一个代理:

#!/usr/bin/env perl6
use v6;

use Test;
plan 2;

class Wizard {
    has Int $!mana;

    method mana() is rw {
        return Proxy.new:
            FETCH => sub ($) { return $!mana },
            STORE => sub ($, $mana) {
                die "It's over 9000!" if ($mana // 0) > 9000;
                $!mana = $mana;
            }
    }
}

my Wizard $gandalf .= new;
$gandalf.mana = 150;
ok $gandalf.mana == 150, 'Updating mana works';
throws_like sub {
    $gandalf.mana = 9001;
}, X::AdHoc, 'Too much mana is too much';

Proxy基本上是一种拦截对存储的读写调用并执行默认行为以外的操作的方法。正如它们的大写所暗示的那样,FETCHPerlSTORE会自动调用它们来解析诸如$gandalf.mana = $gandalf.mana + 5.

在PerlMonks有更全面的讨论,包括你是否应该尝试这个。我建议反对上述 - 以及rw一般的公共属性。它更多地展示了可以用语言表达的内容,而不是有用的工具。

于 2015-07-28T18:16:17.253 回答