在 Perl 中捕获未定义的值访问
Catching undefined value accessing in Perl
如何捕获对成员变量的访问?
$Class1->{Class2}
如果 Class2
字段不存在,是否可以从内部函数中捕获它?
您可以通过提供适当的 getter/setter 方法来环绕您的 class/instance 变量来做到这一点。永远不应该直接访问内部结构,特别是从 class 本身的外部访问(最好不要在 class 内部这样做,除了维护该特定属性的实际方法。这是一个非常基本的示例:
use warnings;
use strict;
package A;
sub new {
my ($class, %args) = @_;
my $self = bless {}, $class;
$self->x($args{x});
$self->y($args{y});
return $self;
}
sub x {
my ($self, $x) = @_;
$self->{x} = $x if defined $x;
return $self->{x} // 1;
}
sub y {
my ($self, $y) = @_;
$self->{y} = $y if defined $y;
return $self->{y} // 2;
}
package main;
my $obj = A->new(x => 5, y => 3);
print $obj->x ."\n";
print $obj->y ."\n";
现在,您可以轻松地做到 print $obj->{x}
,但这就是您的问题所在。如果代码比这复杂得多,并且出于某种原因您想将 x
属性名称更改为 foo
,但保留 x()
方法,会发生什么情况? $obj->{x}
现在将变为 undef
,因为它从未设置过。
始终使用提供的方法访问 class/object 的属性。像这样的封装是 OO 编程的主要内容。
你可以,但你可能不应该。这里的问题是 - 如果您直接访问 class 中的变量......那么您就可以了。您可以通过一些解决方法来防止这种情况发生 - 这就是像 Moose 这样的东西进来的地方。
还有一些稍微老套的技巧,比如由内而外的对象(我认为这不再是常见的做法 - 几年前 Perl Best Practice 提倡它们)或使用匿名哈希来保存状态。
但失败了 - 为什么不使用访问器,并使用 'AUTOLOAD' 自动生成访问器。
#!/usr/bin/env perl
package MyClass;
use strict;
use warnings;
use vars '$AUTOLOAD';
sub AUTOLOAD {
my ( $self ) = @_;
my $subname = $AUTOLOAD =~ s/.*:://r;
if ( $self -> {$subname} ) {
return $self -> {$subname};
}
warn "Sub called $subname was called\n";
return "$subname";
}
sub new {
my ( $class ) = @_;
my $self = {};
bless $self, $class;
}
package main;
use strict;
use warnings;
my $object = MyClass -> new;
$object -> {var} = "fleeg";
print "Undef fiddle was: ", $object -> fiddle,"\n";
print "But 'var' was: ", $object -> var,"\n";
这有同样的问题,因为更改方法名称可能会导致事情中断。然而,它的优点是您可以随心所欲地处理 'invalid' 方法调用。
但实际上 - 显式 'get' 和 'set' 方法是大多数用例的更好选择。
如何捕获对成员变量的访问?
$Class1->{Class2}
如果 Class2
字段不存在,是否可以从内部函数中捕获它?
您可以通过提供适当的 getter/setter 方法来环绕您的 class/instance 变量来做到这一点。永远不应该直接访问内部结构,特别是从 class 本身的外部访问(最好不要在 class 内部这样做,除了维护该特定属性的实际方法。这是一个非常基本的示例:
use warnings;
use strict;
package A;
sub new {
my ($class, %args) = @_;
my $self = bless {}, $class;
$self->x($args{x});
$self->y($args{y});
return $self;
}
sub x {
my ($self, $x) = @_;
$self->{x} = $x if defined $x;
return $self->{x} // 1;
}
sub y {
my ($self, $y) = @_;
$self->{y} = $y if defined $y;
return $self->{y} // 2;
}
package main;
my $obj = A->new(x => 5, y => 3);
print $obj->x ."\n";
print $obj->y ."\n";
现在,您可以轻松地做到 print $obj->{x}
,但这就是您的问题所在。如果代码比这复杂得多,并且出于某种原因您想将 x
属性名称更改为 foo
,但保留 x()
方法,会发生什么情况? $obj->{x}
现在将变为 undef
,因为它从未设置过。
始终使用提供的方法访问 class/object 的属性。像这样的封装是 OO 编程的主要内容。
你可以,但你可能不应该。这里的问题是 - 如果您直接访问 class 中的变量......那么您就可以了。您可以通过一些解决方法来防止这种情况发生 - 这就是像 Moose 这样的东西进来的地方。
还有一些稍微老套的技巧,比如由内而外的对象(我认为这不再是常见的做法 - 几年前 Perl Best Practice 提倡它们)或使用匿名哈希来保存状态。
但失败了 - 为什么不使用访问器,并使用 'AUTOLOAD' 自动生成访问器。
#!/usr/bin/env perl
package MyClass;
use strict;
use warnings;
use vars '$AUTOLOAD';
sub AUTOLOAD {
my ( $self ) = @_;
my $subname = $AUTOLOAD =~ s/.*:://r;
if ( $self -> {$subname} ) {
return $self -> {$subname};
}
warn "Sub called $subname was called\n";
return "$subname";
}
sub new {
my ( $class ) = @_;
my $self = {};
bless $self, $class;
}
package main;
use strict;
use warnings;
my $object = MyClass -> new;
$object -> {var} = "fleeg";
print "Undef fiddle was: ", $object -> fiddle,"\n";
print "But 'var' was: ", $object -> var,"\n";
这有同样的问题,因为更改方法名称可能会导致事情中断。然而,它的优点是您可以随心所欲地处理 'invalid' 方法调用。
但实际上 - 显式 'get' 和 'set' 方法是大多数用例的更好选择。