2009-10-27 4 views
10

Est-il possible de spécifier dynamiquement une classe en Perl et d'accéder à une méthode statique dans cette classe? Cela ne fonctionne pas, mais illustre ce que je voudrais faire:Puis-je accéder à une méthode statique dans une classe spécifiée dynamiquement en Perl?

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

Je sais que je peux le faire:

$class->static_method(); 

et ignorer le nom de classe est passé à static_method, mais je me demande s'il y a une meilleure façon.

+2

upvoted pour me aider gaspille 15 minutes du temps de mon employeur. :) – Ether

+0

er je veux dire dépenser .. productivement bien sûr !! – Ether

Répondre

4

Je ne connais pas d'une manière particulièrement agréable de le faire, mais il y a des façons moins agréables, comme ce programme:

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

J'ai inclus une variable $class, comme ce fut la façon dont vous avez demandé au question, et il illustre comment le nom de classe peut être choisi à l'exécution, mais si vous connaissez la classe à l'avance, vous pouvez tout aussi facilement appeler &{"Test::Class1::static_method"}(1, 2, 3);

Notez que vous devez désactiver strict "refs" si vous l'avez activé.

+0

@Tim Je l'ai maintenant. Désolé pour l'édition inutile. J'ai posté une solution en utilisant la chaîne 'eval' pour montrer une autre façon de le faire en utilisant votre code comme modèle. –

+1

@Sinan Pas un problème! M'a donné une chance d'expliquer plus loin et d'utiliser la fonction "rollback" pour la première fois. :-) – Tim

1

Vous pouvez utiliser la chaîne 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); 

Sortie:

 
C:\Temp> z 
1, 2, 3 

Repères:

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

Sortie:

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

Je me demande maintenant quels sont les avantages/les inconvénients des méthodes 'no strict 'refs'' et' eval'. Les deux signalent la même erreur si le sous-programme n'est pas défini. Du point de vue de la vitesse, la méthode 'no strict 'refs'' semble être le gagnant évident (n'a fait qu'un simple test). – Inshallah

+1

J'aurais aussi tendance à m'écarter de toute solution impliquant 'eval" "'. – Ether

+0

J'ai oublié 'can'. Vous montre à quel point je fais rarement des choses comme ça. –

10

Yup! La façon de le faire avec des restrictions est d'utiliser 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 renvoie une référence à la méthode, undef/faux. Il suffit ensuite d'appeler la méthode avec la syntaxe dereferene.

Il donne:

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

+1 'can' est la façon la moins folle de le faire. Cela implique toujours tout ce que l'héritage ne fait pas: mais cela vous permettra de contourner le comportement $ self/$ classname en passant par $ _ [0]. –

+1

À tout le moins, s'il s'agit d'un module, il est probable qu'il n'y ait pas de module «parent». Je n'en ai jamais vu de semblable, mais un module hors classe qui hérite des méthodes de son parent semble un peu bizarre. Je me demande s'il y a un bon usage ... –

+2

@Robert P: il y a certainement un bon usage pour les fonctions de classe statiques dans un module avec un parent. par exemple. J'en ai écrit un aujourd'hui pour une famille de gestionnaires de cache, où les fonctions statiques de l'enfant contenaient des informations de configuration qui devaient augmenter un comportement dans le parent. – Ether

1

Il y a trois principales façons d'appeler une fonction statique:

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

Vous pouvez définir votre fonction comme ceci:

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

ou comme celui-ci, qui travaille dans les trois scénarios d'appel, et ne subit aucune surcharge du côté de l'appelant comme la solution de Robert P fait:

use UNIVERSAL qw(isa); 

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

Comme toujours avec Perl, there is more than one way to do it.

use strict; 
use warnings; 
{ 
    package Test::Class; 
    sub static_method{ print join(' ', @_), "\n" } 
} 
  • Vous pouvez utiliser la variable %:: spéciale pour accéder à la table des symboles.

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

    no strict 'refs'; 
    my $code = &{"${class}::static_method"}; 
    # or 
    my $code = *{"${class}::static_method"}{CODE}; 
    $code->('Hello','World'); 
    
  • Vous pouvez également utiliser une chaîne eval.

    eval "${class}::static_method('Hello','World')"; 
    
  • Le plus simple dans ce cas, serait d'utiliser UNIVERSAL::can.

    $code = $class->can('static_method'); 
    $code->('Hello','World'); 
    
Questions connexes