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)
该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)
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,确保您没有指定的任何命名值都不存在.它不与正常运作干扰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
) {}
}
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)
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您的类继承,也就是Mu的new。此例程遍历您的类及其祖先,初始化其名称对应于命名参数的属性:
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“吸食”不是一个名为以外的所有命名的参数a为slurpy哈希。如果它不为空,则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)