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

How does one write custom accessor methods in Perl6?

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

如果我有这个class:

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

我能做到:

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

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

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

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

所以如果你需要这样的东西,你就不会陷入困境;这是如何定义的:
(您可以省略 my,但我想向您展示 Rakudo 来源的 actual line

my subset UInt of Int where * >= 0;

您也可以这样做:

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

我想指出 where 约束中的 * >= 0 只是创建 Callable.

的一种捷径

您可以将以下任何一项作为 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 ?*... where * !== 0


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

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

如果要在汇总查看 class 中的所有值时确保值 "makes sense",则需要做更多的工作。
(它可能还需要更多的实现知识)

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
      }
    )
  }
}

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

#!/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 基本上是一种拦截对存储的读取和写入调用并执行默认行为以外的操作的方法。正如它们的大写字母所示,FETCHSTORE 由 Perl 自动调用以解析像 $gandalf.mana = $gandalf.mana + 5.

这样的表达式

PerlMonks 上有更全面的讨论,包括您是否应该尝试这样做。一般来说,我会反对上述 - 和 public rw 属性。它更多的是展示语言可以表达的内容,而不是有用的工具。