2009-10-27 12 views
10

È possibile specificare in modo dinamico una classe in Perl e accedere a un metodo statico in quella classe? Questo non funziona, ma illustra quello che mi piacerebbe fare:Posso accedere a un metodo statico in una classe specificata in modo dinamico in Perl?

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

So che posso fare questo:

$class->static_method(); 

e ignorare il nome della classe passato a static_method, ma mi chiedo se c'è un modo migliore.

+2

upvoted per avermi aiutato a sprecare 15 minuti di tempo del mio datore di lavoro. :) – Ether

+0

er voglio dire spendere .. produttivamente ovviamente !! – Ether

risposta

4

io sono a conoscenza di un particolare bel modo di fare questo, ma ci sono alcuni modi meno piacevoli, come ad esempio questo programma:

#!/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); 
} 

ho incluso una variabile $class, come era così che lei ha chiesto il domanda, e illustra come il nome della classe può essere scelto in fase di esecuzione, ma se si conosce la classe in anticipo, si potrebbe facilmente chiamare &{"Test::Class1::static_method"}(1, 2, 3);

Nota che è necessario spegnere strict "refs" se si dispone di esso.

+0

@Tim Ho capito ora. Ci scusiamo per la modifica non necessaria. Ho pubblicato una soluzione utilizzando la stringa 'eval' per mostrare un altro modo di farlo usando il codice come modello. –

+1

@Sinan Non è un problema! Mi ha dato la possibilità di spiegare ulteriormente e utilizzare la funzione "rollback" per la prima volta. :-) – Tim

1

È possibile utilizzare stringhe 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); 

uscita:

 
C:\Temp> z 
1, 2, 3 

Benchmark:

#!/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); 
    } 
}; 

uscita:

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

Ora mi chiedo quali sono i pro/contro dei metodi "no strict" refs "e" eval ". Entrambi riportano lo stesso errore se la subroutine non è definita. Dal punto di vista della velocità, il metodo 'no strict 'refs' sembra essere il chiaro vincitore (ha fatto solo un semplice test). – Inshallah

+1

Tenderei anche ad allontanarmi da qualsiasi soluzione che implichi 'eval" "'. – Ether

+0

Mi sono dimenticato di 'can'. Ti mostra quanto raramente faccio cose come questa. –

10

Sì! Il modo per farlo con stenografie è usare 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 restituisce un riferimento al metodo, undef/falso. Devi solo chiamare il metodo con la sintassi del dereferene.

Dà:

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

+1 'can' è il modo meno pazzo per farlo. Sostiene ancora l'intera ereditarietà che: non funziona, ma ti permetterà di aggirare l'intero comportamento $ self/$ classname come $ _ [0] comportamento. –

+1

Per lo meno, se si tratta di un modulo, è probabile che non ci sia un modulo "genitore". Non ho visto niente del genere, ma un modulo non di classe che eredita metodi dal suo genitore sembra un po 'strano. Mi chiedo se ci sia un buon uso per questo ... –

+2

@Robert P: c'è sicuramente un buon uso per le funzioni di classe statiche in un modulo con un genitore. per esempio. Ne ho scritto uno solo oggi per una famiglia di gestori di cache, in cui le funzioni statiche del bambino contenevano alcune informazioni di configurazione necessarie per aumentare il comportamento del genitore. – Ether

1

Ci sono tre modi principali per chiamare una funzione statica:

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

Si potrebbe definire la funzione come questa:

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

o come questo, che funziona in tutti e tre gli scenari di chiamata, e non incorre in alcuna testa sul lato del chiamante, come la soluzione di Robert P fa:

use UNIVERSAL qw(isa); 

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

Come sempre con Perl, there is more than one way to do it.

use strict; 
use warnings; 
{ 
    package Test::Class; 
    sub static_method{ print join(' ', @_), "\n" } 
} 
  • È possibile utilizzare la variabile speciale %:: per accedere alla tabella dei simboli.

    my $class = 'Test::Class'; 
    my @depth = split '::', $class; 
    
    my $ref = \%::; 
    $ref = $glob->{$_.'::'} for @depth; # $::{'Test::'}{'Class::'} 
    
    $code = $glob->{'static_method'}; 
    $code->('Hello','World'); 
    
  • Si potrebbe semplicemente utilizzare un symbolic reference;

    no strict 'refs'; 
    my $code = &{"${class}::static_method"}; 
    # or 
    my $code = *{"${class}::static_method"}{CODE}; 
    $code->('Hello','World'); 
    
  • Si potrebbe anche usare una stringa eval.

    eval "${class}::static_method('Hello','World')"; 
    
  • Il più semplice in questo caso, sarebbe quella di utilizzare UNIVERSAL::can.

    $code = $class->can('static_method'); 
    $code->('Hello','World'); 
    
Problemi correlati