Могу ли я получить доступ к статическому методу в динамически указанном классе в Perl? - PullRequest
10 голосов
/ 28 октября 2009

Можно ли динамически указать класс в Perl и получить доступ к статическому методу в этом классе? Это не работает, но иллюстрирует, что я хотел бы сделать:

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

Я знаю, что могу сделать это:

    $class->static_method();  

и игнорирую имя класса, переданное static_method, но мне интересно, есть ли лучший способ.

Ответы [ 5 ]

10 голосов
/ 28 октября 2009

Да! Способ сделать это со стриктурами - использовать 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 возвращает ссылку на метод undef / false. Затем вам просто нужно вызвать метод с синтаксисом разыменования.

Это дает:

    > perl foobar.pl
    yup it can, and it Passed in '' and ran baz!
5 голосов
/ 28 октября 2009

Как всегда с Perl, есть несколько способов сделать это .

use strict;
use warnings;
{
  package Test::Class;
  sub static_method{ print join(' ', @_), "\n" }
}
  • Вы можете использовать специальную переменную %:: для доступа к таблице символов.

    my $class = 'Test::Class';
    my @depth = split '::', $class;
    
    my $ref = \%::;
    $ref = $glob->{$_.'::'} for @depth; # $::{'Test::'}{'Class::'}
    
    $code = $glob->{'static_method'};
    $code->('Hello','World');
    
  • Вы можете просто использовать символическую ссылку ;

    no strict 'refs';
    my $code = &{"${class}::static_method"};
    # or
    my $code = *{"${class}::static_method"}{CODE};
    $code->('Hello','World');
    
  • Вы также можете использовать строку eval.

    eval "${class}::static_method('Hello','World')";
    
  • Простейшим в этом случае будет использование UNIVERSAL::can.

    $code = $class->can('static_method');
    $code->('Hello','World');
    
4 голосов
/ 28 октября 2009

Мне неизвестен особенно хороший способ сделать это, но есть несколько менее приятных способов, таких как эта программа:

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

Я включил переменную $class, так как именно так вы задали вопрос, и она иллюстрирует, как можно выбрать имя класса во время выполнения, но если вы знаете класс заранее, вы могли бы так же легко вызвать &{"Test::Class1::static_method"}(1, 2, 3);

Обратите внимание, что вы должны выключить strict "refs", если он включен.

1 голос
/ 28 октября 2009

Существует три основных способа вызова статической функции:

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

Вы можете определить свою функцию следующим образом:

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

или как этот, который работает во всех трех сценариях вызова и не несет никаких накладных расходов на стороне вызывающего абонента, как это делает решение Роберта П.:

use UNIVERSAL qw(isa);

sub static_method
{
    my $class = shift if $_[0] and isa($_[0], __PACKAGE__);
    # ...
}
1 голос
/ 28 октября 2009

Вы можете использовать строку 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);

Выход:

C:\Temp> z
1, 2, 3

Тесты:

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

Выход:

             Rate     eval      can nostrict
eval      12775/s       --     -94%     -95%
can      206355/s    1515%       --     -15%
nostrict 241889/s    1793%      17%       --
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...