Perl: может ли ref ($ self) когда-либо вызываться в методе возвращать что-либо кроме __PACKAGE__ или undef? - PullRequest
3 голосов
/ 30 октября 2009

У меня есть метод в классе, который мне нужно убедиться, что он вызывается только для экземпляра объекта, а не как метод класса.

Я, вероятно, сделаю что-то вроде этого:

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

Вопросы:

  1. Можно ли предположить, что если ref () вернет not undef, то он вернет текущий пакет?

  2. В идеале я хотел бы вернуться и сделать что-то подобное во всех моих методах проверки работоспособности. Это плохая идея?

  3. Есть ли более изощренный способ делать то, что я хочу?

«Использовать лося» в данном случае неприемлемо. Однако, если вы вынуждены это сказать, скажите, пожалуйста, как лось делает это проще или эффективнее. Я мог бы хотеть включить это в мою собственную объектную систему.

Спасибо!

EDITED чтобы отразить, что ref никогда не возвращает undef, только пустую строку.

РЕДАКТИРОВАТЬ 2 Вот следующий вопрос. Кто-то ниже предложил использовать:

$self->isa(__PACKAGE__)

Но разве это не всегда будет успешным? Если, конечно, вызывающий не делает что-то действительно сумасшедшее как:

MyClass::MyMethod($ref_to_some_other_object)

Ответы [ 3 ]

7 голосов
/ 30 октября 2009

Во-первых, вы, вероятно, должны 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

См. Также perldoc -f ref :

Если указанный объект был благословлен в пакет, то вместо этого возвращается имя этого пакета. Вы можете думать о 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) = @_;
4 голосов
/ 30 октября 2009

Можно ли предположить, что если ref () вернет not undef, то вернет текущий пакет?

номер

my $bar = Bar->new;
Package::Foo::foo($bar);

приведет к foo помещению $bar в $self, а ref $self вернет Bar.

И, как уже отмечалось в предыдущих ответах, проверка буквального имени пакета, а не проверка isa в любом случае нарушает наследование.

0 голосов
/ 30 октября 2009

вы также можете использовать функциональную форму isa, поэтому вам не нужно проверять, чтобы убедиться, что $ self является ссылкой. Конечно, у функционала isa есть предостережение о том, что пакеты не могут переопределить isa, но меня беспокоит, хорошо это или плохо. в моем собственном коде я обычно делаю что-то подобное, что, как мне кажется, имеет более полезную семантику вызова, чем UNIVERSAL :: isa.

sub isa {UNIVERSAL::isa @_ > 1 ? shift : $_, @_}

.....

return unless isa $self => __PACKAGE__;

for (@objects) {
    say $_->name if isa 'Package';
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...