Могу ли я получить доступ к статическому методу в динамически заданном классе в Perl?
Можно ли динамически указать класс в Perl и получить доступ к статическому методу в этом классе? Это не работает, но иллюстрирует, что я хотел бы сделать:
use Test::Class1;
my $class = 'Test::Class1';
$class::static_method();
Я знаю, что могу это сделать:
$class->static_method();
и игнорировать имя класса, переданное static_method, но мне интересно, есть ли лучший способ.
Ответы
Ответ 1
Да! Способ сделать это с помощью ограничений - использовать 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. Затем вам просто нужно вызвать метод с синтаксисом dereferene.
Он дает:
> perl foobar.pl
yup it can, and it Passed in '' and ran baz!
Ответ 2
Как всегда с 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');
Ответ 3
Я не знаю об особенно хорошем способе этого, но есть несколько менее приятных способов, таких как эта программа:
#!/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"
, если вы его включили.
Ответ 4
Вы можете использовать строку 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% --
Ответ 5
Существует три основных способа вызова статической функции:
-
$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
# ...
}
или как это, что работает во всех трех сценариях вызова и не несет никаких накладных расходов на стороне вызывающего абонента, как это делает решение Robert P:
use UNIVERSAL qw(isa);
sub static_method
{
my $class = shift if $_[0] and isa($_[0], __PACKAGE__);
# ...
}