Perl 6:如何检查`new`是否存在无效参数?

sid*_*com 3 constructor parameter-passing perl6

检查是否将无效参数传递给构造函数方法的最简单方法是什么new

use v6;
unit class Abc;

has Int $.a;

my $new = Abc.new( :b(4) );
Run Code Online (Sandbox Code Playgroud)

tim*_*imo 8

ClassX::StrictConstructor模块应该有所帮助.安装它zef install ClassX::StrictConstructor并使用它如下:

use ClassX::StrictConstructor;

class Stricter does ClassX::StrictConstructor {
    has $.thing;
}

throws-like { Stricter.new(thing => 1, bad => 99) }, X::UnknownAttribute;
Run Code Online (Sandbox Code Playgroud)


Bra*_*ert 6

TLDR; 如果你只是担心有人意外键入错误:a(4):b(4),它可能是最好只标示$.a要求.

class ABC {
  has Int $.a is required;
}

ABC.new( :b(4) ); # error
# The attribute '$!a' is required, but you did not provide a value for it.
Run Code Online (Sandbox Code Playgroud)

快速入侵是添加一个子方法TWEAK,确保您没有指定的任何命名值都不存在.它不与正常运作干扰newBUILD,所以正常的类型检查,而不必重新实现他们的工作.

class ABC {
  has Int $.a;

  submethod TWEAK (

    :a($), # capture :a so the next won't capture it

    *%     # capture remaining named
      ()   # make sure it is empty

  ) {}
}
Run Code Online (Sandbox Code Playgroud)

稍微更复杂(但仍然是hacky)的方式应继续适用于子类,并且不需要通过添加更多属性来更新:

class ABC {
  has Int $.a;

  submethod TWEAK (

    *%_    # capture all named

  ) {

    # get the attributes that are known about
    # (should remove any private attributes from this list)
    my \accepted = say self.^attributes».name».subst(/.'!'/,'');

    # ignore any that we know about
    %_{|accepted}:delete;

    # fail if there are any left
    fail "invalid attributes %_.keys.List()" if %_

  }
}
Run Code Online (Sandbox Code Playgroud)


rai*_*iph 5

写一个自定义 new

将此方法声明添加到您的类中:

method new ( :$a is required ) { callsame }
Run Code Online (Sandbox Code Playgroud)

所述:$a结合于命名参数命名a(即,键/值对,其关键是'a',例如。a => 4)。

is required接下来的一个参数名称,使a论点强制性

现在不传递命名参数的调用a将被拒绝:

Abc.new( :b(4) ) ;       # Error: Required named parameter 'a' not passed
Run Code Online (Sandbox Code Playgroud)

new方法的主体调用callsame. 它调用new您的类继承,也就是Munew。此例程遍历您的类及其祖先,初始化其名称对应于命名参数的属性:

Abc.new( :a(4) ) ;       # OK. Initializes new object's `$!a` to `4`
Abc.new( :a(4) ).a.say ; # Displays `4`
Run Code Online (Sandbox Code Playgroud)

UPD:布拉德的回答了一个更简单的方法,只是增加了is required直接向现有属性的声明

has Int $.a is required; # now there's no need for a custom `new`

ABC.new( :b(4) ); # The attribute '$!a' is required...
Run Code Online (Sandbox Code Playgroud)

请注意错误消息从Required named parameter 'a' not passed到的转变attribute '$!a' is required...。这反映了从添加new带有必需的例程参数然后自动绑定到属性的自定义,到直接添加is required属性的转变。

如果这还不够,请继续阅读。

默认的 new

  • 接受任何和所有命名参数(对)。然后它将它们传递给在对象构造期间自动调用的其他例程。您:b(4)在示例代码中传递了命名参数。它被接受并传递。

  • 拒绝任何和所有位置参数(不是成对的)。

new原始代码中的调用被接受,因为您只传递了一个命名参数,所以没有位置参数,从而满足默认直接完成的参数验证new

拒绝未知的命名参数(以及任何位置参数)

method new ( :$a!, *%rest ) { %rest and die 'nope'; callsame }
Run Code Online (Sandbox Code Playgroud)

*%rest“吸食”不是一个名为以外的所有命名的参数aslurpy哈希。如果它不为空,则die触发。

另请参阅timotimo 的回答

需要位置参数而不是命名参数

如上所示,使用命名参数/参数自动初始化相应的对象属性几乎总是既简单又更好。如果您想让人们轻松地从您的类继承并添加他们也想在new.

但是如果你想推翻这个经验法则,你可以要求一个或多个位置参数/参数并调用新对象上的方法以从传递的参数初始化它。

也许最简单的方法是更改​​属性,使其可公开写入:

has Int $.a is rw;
Run Code Online (Sandbox Code Playgroud)

并添加如下内容:

method new ( $a ) { with callwith() { .a = $a; $_ } }
Run Code Online (Sandbox Code Playgroud)

callwith()例程调用new没有参数、位置或命名的继承。默认 ( Mu)new返回一个新构造的对象。.a = $a设置它的a属性并$_返回它。所以:

my $new = Abc.new( 4 );
say $new.a ; # Displays `4`
Run Code Online (Sandbox Code Playgroud)

如果你不想要一个公开的可写属性,那么保持has原样,而是写一些类似的东西:

method !a  ( $a ) { $!a = $a } # Methods starting `!` are private
method new ( $a ) { with callwith() { $_!a($a); $_ } }
Run Code Online (Sandbox Code Playgroud)