我有以下代码:
my $coderef = ${MyModule::MyTool->new};
但是当我尝试
时$coderef->();
我收到了错误:
Not a CODE reference
如何引用构造函数(不调用它)并延迟运行引用的代码?
答案 0 :(得分:11)
${...}
是标量解除引用运算符,而不是匿名子例程构造函数。你想要:
my $coderef = sub {MyModule::MyTool->new};
如果你的构造函数接受了参数,你可以这样写:
my $coderef = sub {MyModule::MyTool->new(@_)};
上面的两个例子没有解决一个问题,那就是保留caller
的功能。如果你的构造函数需要这个(很多没有),你可以使用Perl的魔术goto &sub
语法:
my $coderef = sub {unshift @_, 'MyModule::MyTool'; goto &{$_[0]->can('new')} };
这可能需要一点解释。首先,模块名称放在任何其他参数之前(这是new
方法所期望的)。然后,我使用UNIVERSAL
方法->can
检索new
方法的coderef。 goto &{...}
然后使用当前参数列表跳转到该coderef。
编辑:下面的评论表明,当你需要使用更长的第三种技术时会有一些混乱。以下是显示问题的简短部分:
package Original;
sub new {say +(caller)[0]} # presumably some better use of caller
# or Carp (which uses caller)
package Encapsulate::Simple;
sub new {
my (undef, $invocant, $method) = @_;
sub {$invocant->$method(@_)}
}
package Encapsulate::Better;
sub new {
my (undef, $invocant, $method) = @_;
sub {unshift @_, $invocant; goto &{$invocant->can($method)}}
}
package main;
my $bad = Encapsulate::Simple->new(qw/Original new/);
$bad->(); # always prints 'Encapsulate::Simple'
my $good = Encapsulate::Better->new(qw/Original new/);
$good->(); # prints 'main' as it should
package another;
$bad->(); # erroneously prints 'Encapsulate::Simple' again
$good->(); # prints 'another' as it should
简而言之,Encapsulate::Better
的子保留Original->new
的确切功能,而Encapsulate::Simple
将其永久绑定到其包,打破使用caller
的任何封装方法任何东西。
答案 1 :(得分:1)
使用\&
获取对命名函数的引用:
my $coderef = \&MyModule::MyTool::new;
答案 2 :(得分:-1)
无论哪个包包含新包,这都应该有效:
my $coderef = MyModule::MyTool->UNIVERSAL::can( 'new' );
因此,如果MyModule::MyTool
未实现其构造函数,您仍然可以获得它的处理。