为什么子类不继承其父类的常量?

qod*_*nja 6 oop perl inheritance constants moose

所以我开始关注我的Moosey业务,我认为在我使用数字的地方使用常数可能会很好,以便明确这些数字的含义或以后更改

所以在父类中我添加了标准的'use constant'

package Parent;
    use constant {
        NO_LEVEL => 0,
        MY_LEVEL => 1,
        YOUR_LEVEL => 2,
    };

package Child;
extends 'Parent';

#just to demonstrate that child can or cannot access the constant
sub printMyLevel{
 print MY_LEVEL;
}
Run Code Online (Sandbox Code Playgroud)

但是子类不知道父级中设置的常量!卫生署!

我猜我必须做一些Moose魔法才能让它正常工作,或完全不同.我在这个问题上的搜索没有拉出任何结果= /

Sin*_*nür 11

常量是子程序.

{
    package Parent;
    use Moose;
    use namespace::autoclean;

    use constant {
        NO_LEVEL => 0,
        MY_LEVEL => 1,
        YOUR_LEVEL => 2,
    };
    __PACKAGE__->meta->make_immutable;
};

{
    package Child;
    use Moose;
    use namespace::autoclean;

    extends 'Parent';

    sub printMyLevel {
        my $self = shift;
        my $class = ref $self;

        print $class->MY_LEVEL;
    }
    __PACKAGE__->meta->make_immutable;
}

package main;

my $child = Child->new;
$child->printMyLevel;
Run Code Online (Sandbox Code Playgroud)

请记住,常量是具有空原型的子例程.perl在编译期间利用它来内联它们.但是,方法调用忽略了原型,因此不会内联以这种方式访问​​的可继承常量.

  • 从perl 5.14开始,`package`可以作为一个参数:http://perldoc.perl.org/perl5140delta.html#Syntactical-Enhancements (4认同)
  • +1,非常彻底的回答.我只想补充一点,常量方法明显慢于常量子程序.由于perl的优化器不会内联该值,因此perl每次都必须调用该方法. (2认同)

Ilm*_*nen 6

这实际上是在文档中提到的,如果仅在传递中:

"常量属于它们定义的包.要引用另一个包中定义的常量,请指定完整的包名称,如下所示Some::Package::CONSTANT.常量可以由模块导出,也可以作为类或实例方法调用, is,as Some::Package->CONSTANT$obj->CONSTANTwhere $obj是一个实例Some::Package.子类可以定义自己的常量来覆盖它们的基类中的那些."


Eri*_*rom 4

由于常量是子例程,并且您可以通过调用它们来获得继承,因为方法位已经被覆盖得很死,所以这里有一个不同的旋转。

如果您知道您只在单个文件中工作,则可以使用词法常量来桥接包:

package Parent;
    our ($NO_LEVEL, $MY_LEVEL, $YOUR_LEVEL);
    *NO_LEVEL   = \0;  # this split declaration installs aliases to numbers
    *MY_LEVEL   = \1;  # into the lexicals.  since numbers are constants
    *YOUR_LEVEL = \2;  # to perl, the aliased names are also constants

package Child;

# just to demonstrate that anything below can access the constants
sub printAll {
    print "$NO_LEVEL $MY_LEVEL $YOUR_LEVEL\n";
}

Child->printAll; # 0 1 2

eval {$NO_LEVEL = 3} or print "error: $@\n";
# error: Modification of a read-only value attempted at ...
Run Code Online (Sandbox Code Playgroud)

如果在分配给常量时不需要 perl 死掉,则our声明会变得更简单(并且可以是 a my):

our ($NO_LEVEL, $MY_LEVEL, $YOUR_LEVEL) = (0, 1, 2);
Run Code Online (Sandbox Code Playgroud)

您可以恢复不变的本质,同时仍然使用简洁的语法和一点魔法:

my $constant = sub {Internals::SvREADONLY($_[$_], 1) for 0 .. $#_};

package Parent;
    $constant->(our ($NO_LEVEL, $MY_LEVEL, $YOUR_LEVEL) = (0, 1, 2));

package Child;

# just to demonstrate that anything below can access the constants
sub printAll {
    print "$NO_LEVEL $MY_LEVEL $YOUR_LEVEL\n";  # interpolates :)
}

Child->printAll; # 0 1 2

eval {$NO_LEVEL = 3} or print "error: $@\n";
# error: Modification of a read-only value attempted at ...
Run Code Online (Sandbox Code Playgroud)

您当然可以省略$constantcoderef 并内联魔法:

package Parent;
    Internals::SvREADONLY($_, 1)
        for our ($NO_LEVEL, $MY_LEVEL, $YOUR_LEVEL) = (0, 1, 2);
Run Code Online (Sandbox Code Playgroud)