我想将一些OO包中的subs放入一个数组中 - 也在包中 - 用作调度表.像这样的东西
package Blah::Blah;
use fields 'tests';
sub new {
my($class )= @_;
my $self = fields::new($class);
$self->{'tests'} = [
$self->_sub1
,$self->_sub2
];
return $self;
}
_sub1 { ... };
_sub2 { ... };
Run Code Online (Sandbox Code Playgroud)
我不完全确定这个的语法?
$self->{'tests'} = [
$self->_sub1
,$self->_sub2
];
Run Code Online (Sandbox Code Playgroud)
要么
$self->{'tests'} = [
\&{$self->_sub1}
,\&{$self->_sub2}
];
Run Code Online (Sandbox Code Playgroud)
要么
$self->{'tests'} = [
\&{_sub1}
,\&{_sub2}
];
Run Code Online (Sandbox Code Playgroud)
我似乎无法在OO包中使用它,而在程序方面它非常简单,我没有找到OO的任何示例.
Iain非常感谢任何帮助
Rob*_*t P 11
你的朋友是can
.它返回对子例程的引用(如果存在),否则返回null.它甚至可以正确地走向OO链.
$self->{tests} = [
$self->can('_sub1'),
$self->can('_sub2'),
];
# later
for $tn (0..$#{$self->{tests}}) {
ok defined $self->{tests}[$tn], "Function $tn is available.";
}
# and later
my $ref = $self->{tests}[0];
$self->$ref(@args1);
$ref = $self->{tests}[1];
$self->$ref(@args2);
Run Code Online (Sandbox Code Playgroud)
或者,由于这个问题(恰好是这个问题的变体),你可以直接调用它:
$self->${\$self->{tests}[0]}(@args1);
$self->${\$self->{tests}[1]}(@args1);
Run Code Online (Sandbox Code Playgroud)
请注意,它\
为我们提供了对subref的引用,然后由${}
after 解除引用$self->
.呼!
为了解决大脑提到的及时性问题,另一种方法是简单地让{test}成为一个子程序,返回一个ref,然后你就可以在你需要的时候得到它:
sub tests {
return [
$self->can('_sub1'),
$self->can('_sub2')
];
}
Run Code Online (Sandbox Code Playgroud)
然后使用它:
for $tn (0..$#{$self->tests()}) {
...
}
Run Code Online (Sandbox Code Playgroud)
当然,如果你不得不迭代refs,你也可以直接将参考传递出来:
for my $ref (0..$#{$self->tests()}) {
$self->$ref(@args);
}
Run Code Online (Sandbox Code Playgroud)
虽然罗伯特P的答案可能对你有用,但它有一个问题就是在这个过程中尽早修复调度.我倾向于尽可能晚地解决这些方法,所以我会把tests
数组中的东西留作方法名称,直到你想要使用它们:
$self->{tests} = [
qw( _sub1 _sub2 )
];
Run Code Online (Sandbox Code Playgroud)
动态语言的优势在于,只要您愿意决定将要发生什么,您就可以等待.
当你想运行它们时,你可以经历与罗伯特已经注意到的相同的过程.我会为它添加一个接口:
foreach my $method_name ( $obj->get_test_methods )
{
$obj->$method_name();
}
Run Code Online (Sandbox Code Playgroud)
这可能会更好,因为没有将测试绑定到现有的方法名称:
foreach my $method_name ( $obj->get_test_methods )
{
$obj->run_test_named( $method_name );
}
Run Code Online (Sandbox Code Playgroud)
那run_test_named
可能是你的调度员,它可以非常灵活:
sub run_test_named
{
my( $self, $name ) = @_;
# do anything you want, like in Robert's answer
}
Run Code Online (Sandbox Code Playgroud)
您可能想要做的一些事情:
当您将自己决定要执行的内容与其实现分开时,您可以获得更多自由.不仅如此,下次调用相同的测试名称时,您可以执行不同的操作.
use lib Alpha;
my $foo = Alpha::Foo->new; # indirect object syntax is deprecated
$foo->bar();
my %disp_table = ( bar => sub { $foo->bar() } );
$disp_table{bar}->(); # call it
Run Code Online (Sandbox Code Playgroud)
您需要一个闭包,因为您想将方法调用转换为普通的子例程调用,因此您必须捕获您正在调用该方法的对象.