Подтвердить что ты не робот

Почему я не должен использовать UNIVERSAL:: isa?

В соответствии с этим

http://perldoc.perl.org/UNIVERSAL.html

Я не должен использовать UNIVERSAL:: isa() и вместо этого должен использовать $obj- > isa() или CLASS- > isa().

Это означает, что, чтобы выяснить, является ли что-то ссылкой в ​​первую очередь, а затем ссылается на этот класс, я должен сделать

eval { $poss->isa("Class") }

и проверьте [email protected]и все это gumph, иначе

use Scalar::Util 'blessed';
blessed $ref && $ref->isa($class);

Мой вопрос - почему? Что не так с UNIVERSAL:: isa называется так? Это намного чище для таких вещей, как:

my $self = shift if UNIVERSAL::isa($_[0], __PACKAGE__)

Чтобы узнать, вызывает ли эта функция объект или нет. И есть ли хорошая чистая альтернатива, которая не громоздка с амперсандами и потенциально длинными линиями?

4b9b3361

Ответ 1

Основная проблема заключается в том, что если вы вызываете UNIVERSAL::isa напрямую, вы обходите любые классы, перегруженные isa. Если эти классы полагаются на перегруженное поведение (которое они, вероятно, делают, или иначе они бы не переопределили его), то это проблема. Если вы вызываете isa непосредственно на свой благословенный объект, тогда правильный метод isa вызывается в любом случае (перегружается, если он существует, UNIVERSAL:: if not).

Вторая проблема заключается в том, что UNIVERSAL::isa выполнит только те тесты, которые вы хотите на благословенной ссылке, как и любое другое использование isa. Он имеет другое поведение для не-блаженных ссылок и простых скаляров. Таким образом, ваш пример, который не проверяет, благословен ли $ref, не делает правильные вещи, вы игнорируете условие ошибки и используете альтернативное поведение UNIVERSAL. В определенных обстоятельствах это может вызвать незначительные ошибки (например, если ваша переменная содержит имя класса).

Рассмотрим:

use CGI;

my $a = CGI->new();

my $b = "CGI";

print UNIVERSAL::isa($a,"CGI");  # prints 1, $a is a CGI object.
print UNIVERSAL::isa($b,"CGI");  # Also prints 1!! Uh-oh!!

Итак, вкратце, не используйте UNIVERSAL::isa... Сделайте дополнительную проверку ошибок и вызовите isa прямо на свой объект.

Ответ 2

Смотрите документы для UNIVERSAL:: isa и UNIVERSAL:: can за то, почему вы не должны этого делать.

В двух словах есть важные модули с подлинной необходимостью переопределить 'isa' (например, Test:: MockObject) и если вы называете это функцией, вы нарушаете это.

Я должен сказать, my $self = shift if UNIVERSAL::isa($_[0], __PACKAGE__) не выглядит ужасно чистым для меня - сторонники защиты от Perl будут жаловаться на линейный шум.:)

Ответ 3

Чтобы ответить на ваш вопрос, ответ находится внизу страницы, на которую вы ссылались, а именно: если пакет определяет метод isa, то вызов UNIVERSAL::isa напрямую не вызовет метод пакета isa. Это очень неинтуитивное поведение с точки зрения объектно-ориентированной ориентации.

Остальная часть этого сообщения - это просто больше вопросов о том, почему вы это делаете в первую очередь.

В коде, подобном выше, в каких случаях этот конкретный тест isa завершится с ошибкой? то есть, если это метод, и в этом случае первым аргументом не был бы класс пакета или его экземпляр?

Я спрашиваю об этом, потому что я задаюсь вопросом, есть ли законная причина, почему вы хотите проверить, является ли первый аргумент объектом в первую очередь. т.е. вы просто пытаетесь поймать людей, говорящих FooBar::method вместо FooBar->method или $foobar->method? Я предполагаю, что Perl не предназначен для такого рода занятий, и если люди ошибочно используют FooBar::method, они скоро узнают.

Ваш пробег может отличаться.

Ответ 4

Все остальные сказали вам, почему вы не хотите использовать UNIVERSAL::isa, потому что он ломается, когда вещи перегружаются isa. Если они привыкли перегружать этот особый метод, вы, конечно, хотите его уважать. Конечно, вы могли бы это сделать, написав:

if (eval { $foo->isa("thing") }) {
     # Do thingish things
}

потому что eval гарантирует возврат false, если он выдает исключение, а последнее значение - в противном случае. Но это выглядит ужасно, и вам не нужно писать свой код забавными способами, потому что язык вас хочет. Мы действительно хотим написать просто:

if ( $foo->isa("thing") ) {
     # Do thingish things
}

Для этого нам нужно убедиться, что $foo всегда является объектом. Но $foo может быть строкой, числом, ссылкой, значением undefined или всякими странными вещами. Какой позор Perl не может сделать все объектом первого класса.

О, подождите, это может...

use autobox;   # Everything is now a first class object.
use CGI;       # Because I know you have it installed.

my $x = 5;
my $y = CGI->new;

print "\$x is a CGI object\n" if $x->isa('CGI');   # This isn't printed.
print "\$y is a CGI object\n" if $y->isa('CGI');   # This is!

Вы можете захватить autobox из CPAN. Вы также можете использовать его с лексической областью, поэтому все может быть объектом первого класса только для файлов или блоков, где вы хотите использовать ->isa() без всех дополнительных головных болей. Это также делает намного больше, чем я рассмотрел на этом простом примере.

Ответ 5

Предполагая, что ваш пример того, что вы хотите сделать, находится в объектном методе, вы излишне параноидны. Первый переданный элемент всегда будет либо ссылкой на объект соответствующего класса (или подкласса), либо он будет именем класса (или подкласса). Это никогда не будет ссылкой какого-либо другого типа, если этот метод не был преднамеренно назван функцией. Поэтому вы можете просто использовать ref, чтобы различать два случая.

if (ref $_[0]) {
  my $self = shift;
  # called on instance, so do instancey things
} else {
  my $class = shift;
  # called as a class/static method, so do classy things
}

Ответ 6

Right. Это неправильно для классов, которые перегружают isa. Просто используйте следующую идиому:

if (eval { $obj->isa($class) }) {

Это легко понять и общепризнано.