代码之家  ›  专栏  ›  技术社区  ›  sid_com

Perl 6:如何检查“new”中的无效参数?

  •  4
  • sid_com  · 技术社区  · 7 年前

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

    use v6;
    unit class Abc;
    
    has Int $.a;
    
    my $new = Abc.new( :b(4) );
    
    3 回复  |  直到 7 年前
        1
  •  9
  •   raiph    7 年前

    这个 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;
    
        2
  •  7
  •   Brad Gilbert    7 年前

    太长,读不下去了如果你只是担心有人不小心打错了 :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.
    

    一个快速的方法是添加一个子方法 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 %_
    
      }
    }
    
        3
  •  5
  •   Community CDub    4 年前

    编写自定义 new

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

    method new ( :$a is required ) { callsame }
    

    这个 :$a 绑定到 named argument 已命名 a (即密钥为 'a' ,例如。 a => 4 ).

    这个 is required 参数名称后面的 论点 mandatory .

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

    Abc.new( :b(4) ) ;       # Error: Required named parameter 'a' not passed
    

    您的新 方法调用 callsame . 它称为 您的类继承的,即 Mu 's new . 此例程遍历类及其祖先,初始化名称与命名参数相对应的属性:

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

    UPD: 看见 Brad's answer 对于更简单的方法,只需添加 是必需的 直接发送到 现有属性声明 :

    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... . 这反映了从添加自定义 具有所需的例程 参数 然后自动绑定到属性,只需添加 是必需的 直接发送到 属性 .

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

    默认值

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

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

    这个 原始代码中的调用被接受,因为您只传递了一个命名参数,所以没有位置参数,因此满足了默认情况下直接进行的参数验证 .

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

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

    这个 *%rest “slurps”除一个命名参数外的所有命名参数 变成一个 slurpy hash . 如果不为空,则 die 触发器。

    另请参见 timotimo's answer .

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

    它几乎总是既简单又好用 已命名 参数/参数 自动地 如上所示初始化相应的对象属性。如果您想让folk更容易从您的类继承并添加他们也希望在 .

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

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

    has Int $.a is rw;
    

    并添加如下内容:

    method new ( $a ) { with callwith() { .a = $a; $_ } }
    

    这个 callwith() 例程调用继承的 没有参数,位置或命名。默认值( ) 返回新构造的对象。 .a = $a 设置其 属性和 $_ 返回它。因此:

    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); $_ } }