给定一个包含代表包名称的字符串的变量,我如何调用包的特定子例程?
这是我想出的最接近的事情:
package MyPackage;
sub echo {
print shift;
}
my $package_name = 'MyPackage';
$package_name->echo('Hello World');
1;
这段代码的问题是子程序被作为类方法调用;包名作为第一个参数传入。我想从包名称中调用子例程,而无需隐式传入特殊的第一个参数。
给定一个包含代表包名称的字符串的变量,我如何调用包的特定子例程?
这是我想出的最接近的事情:
package MyPackage;
sub echo {
print shift;
}
my $package_name = 'MyPackage';
$package_name->echo('Hello World');
1;
这段代码的问题是子程序被作为类方法调用;包名作为第一个参数传入。我想从包名称中调用子例程,而无需隐式传入特殊的第一个参数。
听起来您实际上并不想将其称为方法,而是将其称为常规子例程。在这种情况下,您可以使用符号引用:
my $package_name = 'MyPackage';
{
no strict 'refs';
&{ $package_name . '::echo' }( 'Hello World' );
}
Perl 方法调用只是常规的子例程,它将调用者作为第一个值。
use strict;
use warnings;
use 5.10.1;
{
package MyPackage;
sub new{ bless {}, shift } # overly simplistic constructor (DO NOT REUSE)
sub echo{ say @_ }
}
my $package_name = 'MyPackage';
$package_name->echo;
my $object = $package_name->new();
$object->echo; # effectively the same as MyPackage::echo($object)
MyPackage
MyPackage=HASH(0x1e2a070)
如果要调用没有调用者的子例程,则需要以不同的方式调用它。
{
no strict 'refs';
${$package_name.'::'}{echo}->('Hello World');
&{$package_name.'::echo'}('Hello World');
}
# only works for packages without :: in the name
$::{$package_name.'::'}{echo}->('Hello World');
$package_name->can('echo')->('Hello World');
该can
方法返回对子例程的引用,如果在调用者上调用了该子例程,则该引用将被调用。然后可以单独使用 coderef。
my $code_ref = $package_name->can('echo');
$code_ref->('Hello World');
使用有一些注意事项can
:
can
可以被包或它继承的任何类覆盖。
这实际上可能是您正在寻找的行为。
另一种方法是使用一种叫做符号引用的东西。
{
no strict 'refs';
&{ $package_name.'::echo' }('Hello World');
}
通常不建议使用符号引用。部分问题在于,可能会在您不打算使用符号引用的地方意外使用符号引用。这就是为什么你不能use strict 'refs';
生效的原因。
这可能是做你想做的最简单的方法。
如果您不想使用符号引用,可以使用Stash。
$MyPackage::{echo}->('Hello World');
$::{'MyPackage::'}{echo}->('Hello World');
$main::{'MyPackage::'}{echo}->('Hello World');
$main::{'main::'}{'MyPackage::'}{echo}->('Hello World');
$main::{'main::'}{'main::'}{'main::'}{'MyPackage::'}{echo}->('Hello World');
唯一的问题是你必须$package_name
分开::
*Some::Long::Package::Name::echo = \&MyPackage::echo;
$::{'Some::'}{'Long::'}{'Package::'}{'Name::'}{echo}('Hello World');
sub get_package_stash{
my $package = shift.'::';
my @package = split /(?<=::)/, $package;
my $stash = \%:: ;
$stash = $stash->{$_} for @package;
return $stash;
}
get_package_stash('Some::Long::Package::Name')->{echo}('Hello World');
不过这并不是什么大问题。快速浏览 CPAN后,您会发现Package::Stash。
use Package::Stash;
my $stash = Package::Stash->new($package_name);
my $coderef = $stash->get_symbol('&echo');
$coderef->('Hello World');
(纯 Perl版本的Package::Stash使用符号引用,而不是 Stash)
甚至可以为子例程/方法创建别名,就好像从使用Exporter的模块中导入一样:
*echo = \&{$package_name.'::echo'};
echo('Hello World');
我建议限制别名的范围:
{
local *echo = \&{$package_name.'::echo'};
echo('Hello World');
}
这是一个例外,您可以在其中使用strict 'refs'
启用的符号引用。
使用&{ <EXPRESSION> }()
调用名称为表达式的 sub 的语法,如perldoc perlref
列出取消引用运算符时所述:
诚然,在这种情况下使用花括号有点傻,但是 BLOCK 可以包含任意表达式,尤其是下标表达式:
&{ $dispatch{$index} }(1,2,3); # call correct routine
随机实例:
# Note no "use strict"!
use File::Slurp;
my $p="File::Slurp";
@a=&{"${p}::read_file"}(".profile");
print $a[0];