Perl 6:如何检查 `new` 的无效参数?
Perl 6: how to check `new` for invalid arguments?
检查是否将无效参数传递给构造方法的最简单方法是什么new
?
use v6;
unit class Abc;
has Int $.a;
my $new = Abc.new( :b(4) );
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.
一个快速的 hack 是添加一个子方法 TWEAK
以确保您没有指定的任何命名值都不存在。它不会干扰 new
和 BUILD
的正常工作,因此正常类型检查无需重新实现它们即可工作。
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
) {}
}
一种稍微复杂一点(但仍然很老套)的方法,应该继续为子类工作,并且不需要通过添加更多属性来更新:
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 %_
}
}
ClassX::StrictConstructor
module 应该有所帮助。使用 zef install ClassX::StrictConstructor
安装它并像这样使用它:
use ClassX::StrictConstructor;
class Stricter does ClassX::StrictConstructor {
has $.thing;
}
throws-like { Stricter.new(thing => 1, bad => 99) }, X::UnknownAttribute;
编写自定义new
将此方法声明添加到您的 class:
method new ( :$a is required ) { callsame }
:$a
绑定到名为 a
的 named argument(即键为 'a'
的 key/value 对,例如 a => 4
).
参数名称后面的 is required
使 a
参数成为 mandatory.
现在不传递名为 a
的命名参数的调用将被拒绝:
Abc.new( :b(4) ) ; # Error: Required named parameter 'a' not passed
新 new
方法的主体调用 callsame
. It calls the new
that your class inherits, namely Mu
's new
。此例程遍历您的 class 及其祖先初始化名称对应于命名参数的属性:
Abc.new( :a(4) ) ; # OK. Initializes new object's `$!a` to `4`
Abc.new( :a(4) ).a.say ; # Displays `4`
UPD: 请参阅 了解更简单的方法,该方法仅将 is required
直接添加到 现有属性声明 在 class:
has Int $.a is required; # now there's no need for a custom `new`
ABC.new( :b(4) ); # The attribute '$!a' is required...
请注意错误消息中从 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 }
*%rest
将除名为 a
的参数之外的所有命名参数“吞噬”到 slurpy hash 中。如果它不为空,则 die
触发。
另见 。
需要位置参数而不是命名参数
使用namedparameters/arguments来自动初始化相应的对象属性几乎总是更简单和更好,如上所示。如果你想让人们很容易从你的 class 继承并添加他们也希望在 new
期间初始化的属性,这一点尤其正确。
但是如果你想推翻这个经验法则,你可以要求一个或多个 positional parameters/arguments 并调用新对象上的方法来从传递的参数初始化它。
也许最简单的方法是更改属性,使其公开可写:
has Int $.a is rw;
并添加如下内容:
method new ( $a ) { with callwith() { .a = $a; $_ } }
callwith()
例程调用继承的 new
,不带参数、位置参数或命名参数。默认(Mu
)new
returns一个新构造的对象。 .a = $a
设置它的 a
属性和 $_
returns 它。所以:
my $new = Abc.new( 4 );
say $new.a ; # Displays `4`
如果您不想要公开可写的属性,请保持 has
不变,而是编写如下内容:
method !a ( $a ) { $!a = $a } # Methods starting `!` are private
method new ( $a ) { with callwith() { $_!a($a); $_ } }
检查是否将无效参数传递给构造方法的最简单方法是什么new
?
use v6;
unit class Abc;
has Int $.a;
my $new = Abc.new( :b(4) );
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.
一个快速的 hack 是添加一个子方法 TWEAK
以确保您没有指定的任何命名值都不存在。它不会干扰 new
和 BUILD
的正常工作,因此正常类型检查无需重新实现它们即可工作。
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
) {}
}
一种稍微复杂一点(但仍然很老套)的方法,应该继续为子类工作,并且不需要通过添加更多属性来更新:
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 %_
}
}
ClassX::StrictConstructor
module 应该有所帮助。使用 zef install ClassX::StrictConstructor
安装它并像这样使用它:
use ClassX::StrictConstructor;
class Stricter does ClassX::StrictConstructor {
has $.thing;
}
throws-like { Stricter.new(thing => 1, bad => 99) }, X::UnknownAttribute;
编写自定义new
将此方法声明添加到您的 class:
method new ( :$a is required ) { callsame }
:$a
绑定到名为 a
的 named argument(即键为 'a'
的 key/value 对,例如 a => 4
).
参数名称后面的 is required
使 a
参数成为 mandatory.
现在不传递名为 a
的命名参数的调用将被拒绝:
Abc.new( :b(4) ) ; # Error: Required named parameter 'a' not passed
新 new
方法的主体调用 callsame
. It calls the new
that your class inherits, namely Mu
's new
。此例程遍历您的 class 及其祖先初始化名称对应于命名参数的属性:
Abc.new( :a(4) ) ; # OK. Initializes new object's `$!a` to `4`
Abc.new( :a(4) ).a.say ; # Displays `4`
UPD: 请参阅 is required
直接添加到 现有属性声明 在 class:
has Int $.a is required; # now there's no need for a custom `new`
ABC.new( :b(4) ); # The attribute '$!a' is required...
请注意错误消息中从 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 }
*%rest
将除名为 a
的参数之外的所有命名参数“吞噬”到 slurpy hash 中。如果它不为空,则 die
触发。
另见
需要位置参数而不是命名参数
使用namedparameters/arguments来自动初始化相应的对象属性几乎总是更简单和更好,如上所示。如果你想让人们很容易从你的 class 继承并添加他们也希望在 new
期间初始化的属性,这一点尤其正确。
但是如果你想推翻这个经验法则,你可以要求一个或多个 positional parameters/arguments 并调用新对象上的方法来从传递的参数初始化它。
也许最简单的方法是更改属性,使其公开可写:
has Int $.a is rw;
并添加如下内容:
method new ( $a ) { with callwith() { .a = $a; $_ } }
callwith()
例程调用继承的 new
,不带参数、位置参数或命名参数。默认(Mu
)new
returns一个新构造的对象。 .a = $a
设置它的 a
属性和 $_
returns 它。所以:
my $new = Abc.new( 4 );
say $new.a ; # Displays `4`
如果您不想要公开可写的属性,请保持 has
不变,而是编写如下内容:
method !a ( $a ) { $!a = $a } # Methods starting `!` are private
method new ( $a ) { with callwith() { $_!a($a); $_ } }