2008-10-15 12 views
17

De acuerdo con esta¿Por qué no debería usar UNIVERSAL :: isa?

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

que no debería usar UNIVERSAL :: ISA() y en su lugar debería utilizar $ obj-> ISA() o de clase> ISA().

Esto significa que para saber si algo es una referencia en el primer lugar y luego se hace referencia a esta clase que tengo que hacer

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

y comprobar $ @ y todo lo que gumph, o de lo contrario

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

Mi pregunta es ¿por qué? ¿Qué pasa con UNIVERSAL :: isa llamado así? Es mucho más limpio para cosas como:

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

Para ver si esta función se está llamando al objeto o no. ¿Y hay una buena alternativa limpia que no se vuelva engorrosa con los símbolos y las líneas potencialmente largas?

Respuesta

33

El problema principal es que si llama directamente al UNIVERSAL::isa, está evitando cualquier clase que haya sobrecargado isa. Si esas clases se basan en el comportamiento sobrecargado (que probablemente lo hagan o de lo contrario no lo habrían reemplazado), entonces este es un problema. Si invoca isa directamente en su objeto bendecido, se llamará al método correcto isa en ambos casos (sobrecargado si existe, UNIVERSAL :: si no).

El segundo problema es que UNIVERSAL::isa solo realizará la prueba que desee en una referencia bendecida como cualquier otro uso de isa. Tiene un comportamiento diferente para referencias no bendecidas y escalares simples. Entonces su ejemplo que no verifica si $ref ha sido bendecido no está haciendo lo correcto, está ignorando una condición de error y está usando el comportamiento alternativo de UNIVERSAL. En ciertas circunstancias, esto puede causar errores sutiles (por ejemplo, si su variable contiene el nombre de una clase).

considerar:

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!! 

Así, en resumen, no utilizan UNIVERSAL::isa ... hacer la comprobación de errores adicional e invocar isa en su objeto directamente.

+0

UNIVERSAL :: isa NO requiere una referencia bendecida. Funciona muy bien en referencias no autorizadas también (por ejemplo, UNIVERSAL :: isa ([7], 'ARRAY') devuelve 1). Como se explica en los documentos, si le pasa una cadena, la trata como un nombre de clase (como lo demuestra su ejemplo). – cjm

+0

@cjm: Tienes razón, debería aclarar eso. –

+0

Por supuesto, hay un momento en que [en realidad desea el segundo número] (http://www.perlmonks.org/?node_id=576091) ... pero debe tener cuidado con las otras limitaciones, si eso es lo que Estoy tratando de hacer. – Joe

7

Para responder directamente a su pregunta, la respuesta es en la parte inferior de la página que está conectado, a saber, que si un paquete define un método isa, a continuación, llamar directamente UNIVERSAL::isa no va a llamar el paquete isa método. Este es un comportamiento muy poco intuitivo desde el punto de vista de orientación del objeto.

El resto de esta publicación es solo más preguntas sobre por qué estás haciendo esto en primer lugar.

En código como el anterior, en qué casos sería un fracaso que prueba específica isa? es decir, si es un método, ¿en qué caso el primer argumento no sería la clase de paquete o una instancia de la misma?

Lo pregunto porque me pregunto si hay una razón legítima por la que se quiere probar si el primer argumento es un objeto en el primer lugar. es decir, ¿está intentando atrapar a la gente diciendo FooBar::method en lugar de FooBar->method o $foobar->method? Supongo que Perl no está diseñado para ese tipo de mimos, y si las personas usan el FooBar::method por error, lo sabrán muy pronto.

Su kilometraje puede variar.

9

Consulte los documentos para UNIVERSAL::isa y UNIVERSAL::can para saber por qué no debe hacerlo.

En pocas palabras, hay módulos importantes con una necesidad genuina de anular 'isa' (como Test::MockObject), y si lo llama como una función, lo rompe.

Tengo que decir que my $self = shift if UNIVERSAL::isa($_[0], __PACKAGE__) no me parece terriblemente limpio: los defensores de anti-Perl se quejarían del ruido de la línea. :)

+0

Gracias por explicar "por qué no" mejor que yo!+1 –

2

Asumiendo que su ejemplo de lo que quiere hacer es dentro de un método de objeto, está siendo innecesariamente paranoico. El primer elemento pasado siempre será una referencia a un objeto de la clase apropiada (o una subclase) o será el nombre de la clase (o una subclase). Nunca será una referencia de ningún otro tipo, a menos que el método haya sido llamado deliberadamente como una función. Por lo tanto, puede usar de manera segura ref para distinguir entre los dos casos.

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

Todos los demás le ha dicho qué que no quieren usar UNIVERSAL::isa, porque se rompe cuando las cosas se sobrecargan isa. Si han adquirido la costumbre de sobrecargar ese método tan especial, ciertamente querrás respetarlo. Claro, usted podría hacer esto escribiendo:

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

PORQUE eval garantías para devolver false si se produce una excepción, y el último valor de otra manera. Pero eso se ve horrible, y no debería necesitar escribir su código de manera divertida porque el lenguaje lo quiere. Lo que realmente queremos es escribir simplemente:

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

Para hacer eso, tendríamos que asegurarnos de que $foo es siempre un objeto. Pero $foo podría ser una cadena, un número, una referencia, un valor indefinido o todo tipo de cosas raras. Qué vergüenza Perl no puede hacer todo un objeto de primera clase.

Oh, espera, que puede ...

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! 

Usted puede agarrar autobox desde el CPAN. También puede usarlo con alcance léxico, por lo que todo puede ser un objeto de primera clase solo para los archivos o bloques donde quiera usar ->isa() sin todos los dolores de cabeza adicionales. También hace un lote más de lo que he cubierto en este sencillo ejemplo.

+3

Personalmente prefiero utilizar el módulo 'eval {$ foo-> isa (" cosa ")}' en lugar de 'autobox', que también afecta el rendimiento. – codeholic

0

Derecha. Hace una cosa incorrecta para las clases que sobrecargan isa. Simplemente use la siguiente expresión idiomática:

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

Se entiende fácilmente y se acepta comúnmente.

Cuestiones relacionadas