perl 对象在另一个方法中访问自己的属性

yyhrrdl8  于 2022-11-15  发布在  Perl
关注(0)|答案(2)|浏览(154)

假设我有这样的代码:

#!/usr/bin/perl
use warnings;
use strict;

package Guy;
sub new {
        my $type = shift;
        my %params = @_;
        my $self = {};
        $self->{'First'} = $params{'First'};
        $self->{'Middle'} = $params{'Middle'};
        $self->{'Last'} = $params{'Last'};
        bless $self, $type;
}
sub printMe
{
        printf "-----------------------------------\n";
        printf "My type is: \"$self->{type}\"\n";        # Line 18
        printf "First  ::  $self->{First}\n";            # Line 19
        printf "Middle ::  $self->{Middle}\n";           # Line 20
        printf "Last   ::  $self->{Last}\n";             # Line 21
        printf "-----------------------------------\n";
}

package main;
my $dude = Guy->new( 'First' => "John", 'Middle' => "Jacob", 'Last' => "Smith" );
$dude->printMe();

输出为:

me@ubuntu1$ ./toy01.perl
Global symbol "$self" requires explicit package name (did you forget to declare "my $self"?) at ./toy01.perl line 18.
Global symbol "$self" requires explicit package name (did you forget to declare "my $self"?) at ./toy01.perl line 19.
Global symbol "$self" requires explicit package name (did you forget to declare "my $self"?) at ./toy01.perl line 20.
Global symbol "$self" requires explicit package name (did you forget to declare "my $self"?) at ./toy01.perl line 21.
Execution of ./toy01.perl aborted due to compilation errors.
me@ubuntu1$

所以这里的问题是类方法printMe()不能访问存储在$self散列中的属性,而散列是在构造函数中填充的。
在构造函数中,$self被创建为一个哈希值,用来存储传递给构造函数的属性。当$self需要全局时,它是用局部作用域创建的吗?还是在printMe()方法的顶部缺少一个命令,使$self可见?
以及如何引用属性type,该属性是在构造函数中设置的,但没有存储在$self哈希中?(我的$self->{type}调用显然是为了找到这里的解决方案而进行的绝望尝试。)
任何建议或反馈都是在这里感谢你

nhaq1z21

nhaq1z211#

对象会当做第一个参数传送至方法,但您必须自行填入变数:

sub printMe
{
    my ($self) = @_;
    ...

请注意,$self->{type}不存在。请使用Scalar::Util中的ref $selfblessed($self)
此外,使用printf时不带格式字符串也是无用的。请改用printsay,或者正确使用printf

printf "First  ::  %s\n", $self->{First};
p8ekf7hl

p8ekf7hl2#

你已经得到了一个很好的“如何解决这个问题”的答案,你已经接受了这个答案,但可能值得更深入地挖掘出问题的根源。
您有两个子例程(它们实际上是方法,但在Perl中基本上是相同的):

sub new {
  ...;
  my $self = {};

  # code populating $self
  ...;

  bless $self, $type;
}

还有:

sub printMe {

  # code accessing $self

}

new()中,你用my声明$self,这绝对是正确的做法,但是my的作用是创建一个“词法”变量--这个变量只在你声明它的代码块中可见(在new()子例程中)。
当你试图在printMe()子例程中访问$self时,你会得到一个警告--因为那个变量根本不存在于那个子例程中。但是很明显,你需要在printMe()中访问那个变量(或者,至少,那个值)。那么,这是如何工作的呢?
Perl附带了很多文档,perlobj手册页解释了内置的对象系统是如何工作的。
构造函数部分有一个编写new()方法的例子。它和你的方法很相似。关键的代码行是这样的:

return $self;

这表明构造函数方法需要返回你的祝福对象。你的构造函数中的等价行是这样的:

bless $self, $type;

你用了一种稍微不同的方式,但没关系。它都按预期工作。这是你的方法的最后一行,一个Perl子例程将返回最后一次表达式求值的结果。所以这就像写(稍微显式一点):

return bless $self, $type;

好了,你已经从构造函数中返回了对象,并且把它存储在$dude变量中:

my $dude = Guy->new(...);

现在我们需要把这个对象放回printMe()方法中,这样就可以使用它了。perlobj手册页有一个关于方法调用的部分,其中包括:
在对象上呼叫方法会写成$object->method
还有:
当你调用一个方法时,箭头左边的内容作为第一个参数传递给该方法。
如果你读了名为“方法就是简单的子例程”的部分,你会看到:
一个方法的特殊之处在于它期望接收一个对象或一个类名作为它的第一个参数。
下面是一个示例方法:

sub save {
    my $self = shift;

    open my $fh, '>', $self->path() or die $!;
    print {$fh} $self->data()       or die $!;
    close $fh                       or die $!;
}

看,还有一个$self变量,我们将对象传递给子例程(隐式地,使用$object->method()语法),并将对象存储在一个名为$self的新变量中,该变量也是用my声明的,因此也只能在子例程中使用。
回顾代码,您正确地调用了该方法:

$dude->printMe();

唯一的问题是你没有在printMe()方法中设置$self变量。因此,要解决这个问题,你只需在方法的开头添加以下行:

my $self = shift;

对不起...那比我预期的要长得多。希望有用。

相关问题