2009-10-27 10 views
10

¿Es posible especificar dinámicamente una clase en Perl y acceder a un método estático en esa clase? Esto no funciona, pero ilustra lo que me gustaría hacer:¿Puedo acceder a un método estático en una clase especificada dinámicamente en Perl?

use Test::Class1; 
    my $class = 'Test::Class1'; 
    $class::static_method();  

Sé que puedo hacer esto:

$class->static_method(); 

e ignorar el nombre de la clase pasó a static_method, pero me pregunto si hay una mejor manera.

+2

upvoted por ayudarme a perder 15 minutos de tiempo de mi empleador. :) – Ether

+0

er me refiero a gastar .. productivamente por supuesto !! – Ether

Respuesta

4

tengo conocimiento de un particularmente agradable manera de hacer esto, pero hay algunas maneras menos agradables, como este programa:

#!/usr/bin/perl -w 

use strict; 

package Test::Class1; 

sub static_method { 
    print join(", ", @_) . "\n"; 
} 

package main; 

my $class = "Test::Class1"; 

{ 
    no strict "refs"; 
    &{${class}. "::static_method"}(1, 2, 3); 
} 

He incluido una variable $class, ya que era la forma en que pidió la pregunta, e ilustra cómo se puede elegir el nombre de la clase en tiempo de ejecución, pero si conoce la clase de antemano, puede llamar fácilmente al &{"Test::Class1::static_method"}(1, 2, 3);

Tenga en cuenta que debe desactivar strict "refs" si lo tiene activado.

+0

@Tim lo entiendo ahora. Perdón por la edición innecesaria. Publiqué una solución usando string 'eval' para mostrar otra forma de hacerlo usando tu código como plantilla. –

+1

@Sinan ¡No hay problema! Me dio la oportunidad de explicar más y usar la función "deshacer" por primera vez. :-) – Tim

1

Puede utilizar cuerdas eval:

#!/usr/bin/perl 

use strict; use warnings; 

package Test::Class1; 

sub static_method { 
    print join(", ", @_) . "\n"; 
} 

package main; 

my $class = 'Test::Class1'; 
my $static_method = 'static_method'; 

my $subref = eval q{ \&{ "${class}::${static_method}" } }; 
$subref->(1, 2, 3); 

Salida:

 
C:\Temp> z 
1, 2, 3 

Puntos de referencia:

#!/usr/bin/perl 

use strict; use warnings; 

package Test::Class1; 

sub static_method { "@_" } 

package main; 

use strict; use warnings; 
use Benchmark qw(cmpthese); 

my $class = 'Test::Class1'; 
my $static_method = 'static_method'; 

cmpthese -1, { 
    'can' => sub { my $r = $class->can($static_method); $r->(1, 2, 3) }, 
    'eval' => sub { 
     my $r = eval q/ \&{ "${class}::${static_method}" } /; 
     $r->(1, 2, 3); 
    }, 
    'nostrict' => sub { 
     no strict "refs"; 
     my $r = \&{ "${class}::static_method" }; 
     $r->(1, 2, 3); 
    } 
}; 

Salida:

 
      Rate  eval  can nostrict 
eval  12775/s  --  -94%  -95% 
can  206355/s 1515%  --  -15% 
nostrict 241889/s 1793%  17%  -- 
+1

Me pregunto ahora cuáles son los pros/contras de los métodos 'no strict 'refs'' y' eval'. Ambos informan el mismo error si la subrutina no está definida. Desde la perspectiva de la velocidad, el método 'no strict 'refs'' parece ser el claro ganador (hizo solo una prueba simple). – Inshallah

+1

También me inclinaría lejos de cualquier solución que implique 'eval" "'. – Ether

+0

Me olvidé de 'puede'. Te muestra lo raro que hago cosas como esta. –

10

¡Sí! La forma de hacerlo con restricciones es usar can.

package Foo::Bar; 
use strict; 
use warnings; 

sub baz 
{ 
    return "Passed in '@_' and ran baz!"; 
} 

package main; 
use strict; 
use warnings; 

my $class = 'Foo::Bar'; 

if (my $method = $class->can('baz')) 
{ 
    print "yup it can, and it "; 
    print $method->(); 
} 
else 
{ 
    print "No it can't!"; 
} 

can devuelve una referencia al método, undef/falso. Luego solo tiene que llamar al método con la sintaxis dereferene.

Da:

 
    > perl foobar.pl 
    yup it can, and it Passed in '' and ran baz! 
+1

+1 'can' es la forma menos loca de hacerlo. Todavía incurre en la herencia completa que :: does not do, pero le permitirá sortear el comportamiento de $ self/$ classname como $ _ [0]. –

+1

Por lo menos, si se trata de un módulo, es probable que no haya un módulo "principal". No he visto nada de eso, pero un módulo que no es de clase hereda los métodos de su padre parece un poco extraño. Hace que me pregunte si hay un buen uso para ello ... –

+2

@Robert P: definitivamente hay un buen uso para las funciones de clase estáticas en un módulo con un elemento primario. p.ej. Escribí uno solo hoy para una familia de manejadores de caché, donde las funciones estáticas en el niño contenían alguna información de configuración que necesitaba aumentar algún comportamiento en el padre. – Ether

1

Hay tres formas principales para llamar a una función estática:

  • $object->static_method()
  • Classname->static_method()
  • Classname::static_method()

Se podría definir su función como esta:

# callable as $object->static_method() or Classname->static_method() 
sub static_method 
{ 
    my $class = shift; # ignore; not needed 
    # ... 
} 

o como este, que funciona en los tres escenarios del llamamiento, y no incurre en ningún tipo de gastos por parte de la persona que llama como la solución de Robert P hace:

use UNIVERSAL qw(isa); 

sub static_method 
{ 
    my $class = shift if $_[0] and isa($_[0], __PACKAGE__); 
    # ... 
} 
5

Como siempre con Perl, there is more than one way to do it.

use strict; 
use warnings; 
{ 
    package Test::Class; 
    sub static_method{ print join(' ', @_), "\n" } 
} 
  • Puede utilizar el %:: variable especial para acceder a la tabla de símbolos.

    my $class = 'Test::Class'; 
    my @depth = split '::', $class; 
    
    my $ref = \%::; 
    $ref = $glob->{$_.'::'} for @depth; # $::{'Test::'}{'Class::'} 
    
    $code = $glob->{'static_method'}; 
    $code->('Hello','World'); 
    
  • Se podía utilizar simplemente un symbolic reference;

    no strict 'refs'; 
    my $code = &{"${class}::static_method"}; 
    # or 
    my $code = *{"${class}::static_method"}{CODE}; 
    $code->('Hello','World'); 
    
  • También es posible usar una cadena eval.

    eval "${class}::static_method('Hello','World')"; 
    
  • El más simple en este caso, sería el uso de UNIVERSAL::can.

    $code = $class->can('static_method'); 
    $code->('Hello','World'); 
    
Cuestiones relacionadas