Perl中AUTOLOAD的默认参数是什么?

Bel*_*day 1 oop perl autoload

我一直在玩AUTOLOADPerl创建我的访问器,我遇到了这种困惑(我已经搜索过google和perldoc).

我有这个代码:

package Class;
sub new {
..code for constructor here.
}

sub AUTOLOAD {
 my $name= shift;
 print $name;
}
Run Code Online (Sandbox Code Playgroud)

但是,当我做类似的事情时:my $a=Class->new;自动加载子程序仍然执行,并打印Class=HASH(some weird number);

我以为AUTOLOAD只在有未定义的方法或子程序时运行?

我也是这样做的:

my $class = our $AUTOLOAD;

print $class #prints ::DESTROY
Run Code Online (Sandbox Code Playgroud)

当我DESTROY没有传递未定义的函数时,我认为这是$ AUTOLOAD的值,我是对的吗?

amo*_*mon 8

使用Autoload本身就很困难.如果你想要一个为你提供访问器的固体对象系统,那么请使用Moose,Mouse,Moo,或者只是遍历你的字段并自己安装访问器:

BEGIN {
  my @fields = qw/foo bar baz/;
  for my $field (@fields) {
    no strict 'refs';
    # install a closure in the package stash.
    *{ __PACKAGE__ . "::" . $field } = sub {
      my $self = shift;
      $self->{$field} = shift if @_;
      return $self->{$field};
    };
  }
}
Run Code Online (Sandbox Code Playgroud)

如果一个类可以AUTOLOAD遇到未定义的方法,AUTOLOAD则使用缺少的sub的参数调用sub.请求的子的完全限定名称在$AUTOLOAD包变量中传递.

一个典型的Autoload子看起来像:

use Carp;

my %fields_allowed = map {$_ => 1} qw/foo bar baz/;

sub AUTOLOAD {
  my $field = our $AUTOLOAD;
  $field =~ s/.*:://; # strip the package name
  $fields_allowed{$field}
    or croak qq(Can't locate object method $field via package "@{[__PACKAGE__]}");
  my $self = shift;
  $self->{$field} = shift if @_;
  return $self->{$field};
}
Run Code Online (Sandbox Code Playgroud)

还有两个问题:

  • 当对象的引用计数降为零时,或者当线程终止时,DESTROY如果对象提供了一个方法,则在对象上调用该方法.我们可以DESTROY通过提供一个空实现来阻止自动加载:sub DESTROY {}.
  • 我们可以询问任何对象是否可以执行某种方法,例如say "Good dog" if $dog->can("roll").因此,我们必须覆盖can以支持我们的自动加载.该can方法对于安全的鸭子打字很有用.每个对象都继承自UNIVERSAL,它为can和提供默认实现isa.

合同can是它采用方法的名称.它将undef在对象无法执行该方法时返回,或者如果可以,则返回该方法的代码引用.一个合适的实施方案是

sub can {
  my ($self, $name) = @_;

  # check if it's a field of ours
  if ($fields_allowed{$name}) {
    return sub {
      my $self = shift;
      $self->{$name} = shift if @_;
      return $self->{$name};
    };
  }

  # Ask SUPER implementation of can if we can do $name
  if (my $meth = $self->SUPER::can($name)) {
    return $meth;
  }
  return; # no method found
}
Run Code Online (Sandbox Code Playgroud)

我们现在可以简化AUTOLOAD

sub AUTOLOAD {
  my $field = our $AUTOLOAD;
  $field =~ s/.*:://; # strip the package name
  my $code = $self->can($field)
    or croak qq(Can't locate object method $field via package "@{[__PACKAGE__]}");
  goto &$code; # tail call; invisible via `caller()`.
}
Run Code Online (Sandbox Code Playgroud)

要做到这一点很复杂.结论:不要使用Autoload,因为您认为它可能不那么重要.它永远不会.它对于实现代理模式非常有用,但这有点高级.

在深入了解Perl独特而奇特的功能之前,我强烈建议您使用OO基础知识和Moose对象系统.