如何使用元对象协议向对象添加属性?

时间:2018-08-19 17:16:31

标签: class perl6

我试图回答this问题,并认为我可以使用元对象协议为类添加属性。这是一个最小的示例,在此示例中,我尝试在构造后向类test添加属性Configuration

use v6;

class Configuration {
}

my $config = Configuration.new;
my $attr = Attribute.new(
    :name('$.test'), # Trying to add a "test" attribute
    :type(Str),
    :has_accessor(1), 
    :package(Configuration)
);
$config.^add_attribute( $attr );
$config.^compose();
say "Current attributes: ", join ', ', $config.^attributes();
$attr.set_value( $config, "Hello" ); # <-- This fails with no such attribute '$.test'
say $config.test;

运行此命令时,我得到:

Current attributes: $.test
P6opaque: no such attribute '$.test' on type Configuration in a Configuration when trying to bind a value
  in block <unit> at ./p.p6 line 16

1 个答案:

答案 0 :(得分:9)

不能在类组合时间之后添加属性,类组合时间是在编译程序时达到结束}的编译时添加的。 (P6opaque表示形式就是这种情况。可能存在允许这种表示形式的表示形式并非没有可能,但目前尚未指定。)

此外,在元对象上调用.^add_attribute,对于class,属性是按类型而不是按对象的;代码结构表明也许期望是针对每个对象的。没有什么使得不可能具有原型面向对象的(实际上,MOP是经过设计的,因此有人可以在Perl 6中实现这样的对象系统),但是在Perl 6本身中也没有提供任何指定的东西。

因此,对于提供的对象系统,此类操作需要在编译时和关闭}之前完成。可以通过以下方式实现:

class Configuration {
    BEGIN {
        my $attr = Attribute.new(
            :name('$!test'), # Trying to add a "test" attribute
            :type(Str),
            :has_accessor(1),
            :package(Configuration)
        );
        Configuration.^add_attribute( $attr );
    }
}

my $config = Configuration.new;
say "Current attributes: ", join ', ', $config.^attributes();
$config.^attributes[0].set_value( $config, "Hello" );
say $config.test;

这是Perl 6动态的众多地方之一,主要是通过邀请程序员参与编译时,而不是通过在运行时使所有事情变为可能。

最后,我将注意到有一种方法可以将属性添加到现有对象中,并以每个对象为基础:通过使用does在其中混合角色。通过沿途更改对象的类型来起作用。 does here上有一些文档。