【问题标题】:How does one write custom accessor methods in Perl6?如何在 Perl6 中编写自定义访问器方法?
【发布时间】:2015-10-18 08:44:26
【问题描述】:

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

如果我有这门课:

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

我可以这样做:

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

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

【问题讨论】:

  • 如果你使用<!-- language-all: lang-perl6 -->,你只需要一次。如果您想在每个代码块中更改它,请在其前面使用<!-- language: lang-perl6 -->

标签: raku


【解决方案1】:

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

【讨论】:

  • 感谢您精疲力尽的回答。我接受了另一个,因为它更短,可能会为谷歌的人节省一些时间。基本上,我想要你答案的最后一部分——FETCH/STORE 结构。抱歉,如果我的问题不清楚。
  • @AdamLibuša 经常使用 Perl 6 的人会假设很多并提出错误的问题,所以我为通常应该问的问题写了一个答案。一旦我看到另一个答案,我就认为它会成为被接受的答案,这很好。
【解决方案2】:

您可以通过声明方法is rw 来获得与$.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 进行了更全面的讨论,包括您是否应该尝试这样做。我建议反对上述 - 以及一般的公共rw 属性。与其说是一种有用的工具,不如说是一种语言表达的可能性。

【讨论】:

  • 谢谢 - 这个 FETCH & STORE 结构正是我想要的。不过有点难看——很多样板。我期待 C# get/set 块样式中的某些内容。如果没有容易编写的自定义访问器方法,属性会退化为公共属性。
  • @raiph 我想我会编辑问题的文本。我真的在寻找一种通用的电动工具(问题的标题暗示了这一点,与问题的文本不同)。我同意你的观点,在 90% 的情况下,where 是正确的工具。我不想解决提出的问题,我想了解工具,使用的示例只是一个示例。
  • @raiph 在某些情况下,您想做一些肮脏的事情,例如,在调试期间记录每个 getter 使用情况,而忽略 setter 使用情况。很高兴知道如何做。
  • 看起来throws_like 实际上是,或者已经重命名为throws-like
  • 根据这篇文章,这实际上似乎是一个深思熟虑的决定,而不是让创建 C# 风格的自定义访问器在语法上变得容易。太糟糕了。 6guts.wordpress.com/2016/11/25/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-03
  • 1970-01-01
  • 2017-06-11
  • 1970-01-01
相关资源
最近更新 更多