如何对构造函数进行代码引用?

时间:2010-11-19 21:33:07

标签: perl

我有以下代码:

my $coderef = ${MyModule::MyTool->new};

但是当我尝试

$coderef->();

我收到了错误:

Not a CODE reference

如何引用构造函数(不调用它)并延迟运行引用的代码?

3 个答案:

答案 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未实现其构造函数,您仍然可以获得它的处理。