我在类中有方法,我需要确保只在对象实例上调用,而不是作为类方法。
我可能会做这样的事情:
# Edit: this is terrible, don't do this, it breaks inheritance.
sub foo
{
my ($self) = @_;
if (ref($self) ne __PACKAGE__) { return; }
...do stuff
}
但我认为这样做会更有效率:
sub foo
{
my ($self) = @_;
if (not ref($self)) { return; }
...do stuff
}
问题:
是否可以安全地假设如果ref()返回不是undef它将返回当前包?
我希望在我的所有健全性检查方法中回过头来做这样的事情。这是个坏主意吗?
是否有更多的方式来做我想做的事情?
谢谢!
已编辑以反映ref永远不会返回undef,只返回一个空字符串。
编辑2 以下是一个跟进问题。以下有人建议使用:
$self->isa(__PACKAGE__)
但这不会一直成功吗?当然,除非呼叫者做了像以下那样愚蠢的事情:
MyClass::MyMethod($ref_to_some_other_object)
答案 0 :(得分:7)
首先,您应该croak
而不是默默无闻,因为根据您的规范,将foo
称为类方法是违反合同的行为。
其次,只是检查第一个参数是否是参考就足够了。如果涉及继承,您的方法将失败:
#!/usr/bin/perl
package A;
use Carp;
sub new { bless {} => shift }
sub foo {
croak "I am not in " . __PACKAGE__ unless __PACKAGE__ eq ref(shift)
}
package B;
use base 'A';
package main;
$x = B->new;
$x->foo;
C:\Temp> t I am not in A at C:\Temp\t.pl line 19
如果引用的对象已被保存到包中,则返回该包名称。您可以将
ref
视为typeof
运营商。
所以:
sub foo {
croak "Don't call as class method" unless ref shift;
}
最后,请注意ref
从不 会返回undef
。
将此检查添加到每个方法是否是个好主意?我猜一个人可以通过契约观点从设计中得出这个论点。
另一方面,我的方法假设它们被称为实例方法,并且我只检查方法被调用为类方法的可能性,如果该方法在被调用时可以提供有意义的替代行为。
我不记得任何有这些检查的模块。
顺便说一句,而不是
sub foo {
my ($self) = @_;
你应该使用
sub foo {
my $self = shift;
只留下@_
中方法的参数要解压缩。或者,你应该一举解开所有论点:
sub foo {
my ($self, $bar, $baz) = @_;
答案 1 :(得分:4)
假设如果ref()返回不是undef它会返回当前包吗?
是否安全?
没有
my $bar = Bar->new;
Package::Foo::foo($bar);
将导致foo
将$bar
置于$self
,ref $self
将返回Bar
。
并且,正如前面的答案中已经提到的,检查文字包名而不是测试isa
无论如何都会中断继承。
答案 2 :(得分:0)
你也可以使用isa的功能形式,这样你就不必检查以确保$ self是一个参考。当然,功能性isa有一个警告,包装不能覆盖isa,但我被撕裂,如果这是一个好的或坏的事情。在我自己的代码中,我通常做这样的事情,我发现它比UNIVERSAL :: isa有更多有用的调用语义。
sub isa {UNIVERSAL::isa @_ > 1 ? shift : $_, @_}
.....
return unless isa $self => __PACKAGE__;
for (@objects) {
say $_->name if isa 'Package';
}