2

I've been playing with AUTOLOAD to create my accessors in Perl and I have encountered this confusion (I have searched google and perldoc already).

I have this code:

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

sub AUTOLOAD {
 my $name= shift;
 print $name;
}

But when I do something like : my $a=Class->new; The autoload subroutine still executes, and prints Class=HASH(some weird number);

I thought AUTOLOAD only runs when there is an undefined method or subroutine?

And also I did this:

my $class = our $AUTOLOAD;

print $class #prints ::DESTROY

Am I right when I assumed that DESTROY is the value of $AUTOLOAD when no undefined function is passed?

4

1 回答 1

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};
    };
  }
}

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

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

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};
}

仍然存在两个问题:

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

的约定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
}

我们现在可以简化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()`.
}

要做到这一点非常复杂。结论:不要使用 Autoload,因为您认为它可能会减少工作量。从来都不是。它对于实现代理模式非常有用,但这有点高级。

我敦促您在深入了解 Perl 独特而奇怪的特性之前,先了解一下 OO 基础知识和 Moose 对象系统。

于 2013-06-20T07:23:51.193 回答