strawberry - perl tutorial
¿Por qué no debería usar UNIVERSAL:: isa? (6)
De acuerdo a esto
http://perldoc.perl.org/UNIVERSAL.html
No debería usar UNIVERSAL :: isa () y debería usar $ obj-> isa () o CLASS-> isa ().
Esto significa que para saber si algo es una referencia en primer lugar y luego se hace referencia a esta clase, tengo que hacer
eval { $poss->isa("Class") }
y marque $ @ y toda esa maldición, o bien
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 llama 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?
Asumiendo que tu ejemplo de lo que quieres hacer es dentro de un método de objeto, estás 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 ref con seguridad 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
}
Para responder directamente a su pregunta, la respuesta se encuentra en la parte inferior de la página a la que se enlazó, es decir, si un paquete define un método isa
, llamar a UNIVERSAL::isa
directamente no llamará al paquete por un 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 un código como el anterior, ¿en qué casos fallaría esa prueba específica? es decir, si se trata de un método, en cuyo caso ¿el primer argumento no sería la clase de paquete o una instancia de la misma?
Pregunto esto porque me pregunto si existe una razón legítima por la que querría probar si el primer argumento es un objeto en primer lugar. es decir, ¿está tratando de atrapar a la gente diciendo el FooBar::method
lugar del FooBar->method
o el FooBar->method
$foobar->method
? Supongo que Perl no está diseñado para ese tipo de mimos, y si las personas usan el FooBar::method
lo descubrirán lo suficientemente pronto.
Su experiencia puede ser diferente.
El problema principal es que si llamas a UNIVERSAL::isa
directamente, estarás omitiendo 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 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
es 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!!
Entonces, en resumen, no use UNIVERSAL::isa
... Haga la comprobación de error adicional e invoque isa
en su objeto directamente.
Consulte los documentos de UNIVERSAL :: isa y UNIVERSAL :: can para saber por qué no debería hacerlo.
En pocas palabras, hay módulos importantes con una necesidad genuina de reemplazar ''isa'' (como Test :: MockObject ), y si lo llamas como una función, rompes esto.
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 por el ruido de la línea. :)
Todos los demás te han dicho por qué no quieres usar UNIVERSAL::isa
, porque se rompe cuando las cosas se sobrecargan. Si han adquirido la costumbre de sobrecargar ese método tan especial, ciertamente querrás respetarlo. Claro, puedes hacer esto escribiendo:
if (eval { $foo->isa("thing") }) {
# Do thingish things
}
porque eval
garantiza devolver falso si arroja una excepción, y el último valor de lo contrario. 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 solo:
if ( $foo->isa("thing") ) {
# Do thingish things
}
Para hacer eso, deberíamos asegurarnos de que $foo
sea 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é lástima que Perl no pueda convertir todo en un objeto de primera clase.
Oh, espera, 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!
Puede tomar autobox del 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 mucho más de lo que he cubierto en este simple ejemplo.
Derecha. Hace una cosa incorrecta para las clases que sobrecargan isa
. Solo usa el siguiente modismo:
if (eval { $obj->isa($class) }) {
Es fácil de entender y comúnmente aceptado.