问题描述
我有以下代码:
我的 $coderef = ${MyModule::MyTool->new};但是当我尝试
$coderef->();我有错误:
不是 CODE 参考如何引用构造函数(不调用它)并延迟运行引用的代码?
${...}
是标量解引用运算符,而不是匿名子例程构造函数.你想要:
my $coderef = sub {MyModule::MyTool->new};
如果你的构造函数接受参数,你可以这样写:
my $coderef = sub {MyModule::MyTool->new(@_)};
上面的两个例子没有解决一个问题,那就是保留了caller
的功能.如果你的构造函数需要这个(很多不需要),你可以使用 Perl 的魔法 goto &sub
语法:
my $coderef = sub {unshift @_, 'MyModule::MyTool';转到 &{$_[0]->can('new')} };
这可能需要一些解释.首先,模块名称放在任何其他参数之前(这是 new
方法所期望的).然后我使用 UNIVERSAL
方法 ->can
来检索 new
方法的 coderef.goto &{...}
然后使用当前参数列表跳转到那个 coderef.
下面的评论表明对于何时需要使用更长的第三种技术存在一些混淆.这是显示问题的一小段:
包原件;sub new {say +(caller)[0]} # 大概是调用者更好的用法# 或 Carp(使用调用者)封装封装::简单;子新{我的 (undef, $invocant, $method) = @_;sub {$invocant->$method(@_)}}封装封装::更好;子新{我的 (undef, $invocant, $method) = @_;sub {unshift @_, $invocant;转到 &{$invocant->can($method)}}}包主;我的 $bad = Encapsulate::Simple->new(qw/Original new/);$坏->();# 总是打印 'Encapsulate::Simple'我的 $good = Encapsulate::Better->new(qw/Original new/);$good->();# 按原样打印main"打包另一个;$坏->();# 再次错误地打印 'Encapsulate::Simple'$good->();# 按原样打印another"
简而言之,Encapsulate::Better
的 sub 保留了 Original->new
的确切功能,而 Encapsulate::Simple
将它永久绑定到它的包,破坏任何使用 caller
的封装方法.
I have following code:
my $coderef = ${MyModule::MyTool->new};
but when I try
$coderef->();
i got error:
Not a CODE reference
How can I take reference to constructor (without calling it) and run referenced code late?
The ${...}
is the scalar dereference operator, not the anonymous subroutine constructor. You want:
my $coderef = sub {MyModule::MyTool->new};
And if your constructor takes arguments, you could write it this way:
my $coderef = sub {MyModule::MyTool->new(@_)};
The two examples above do not address one issue, and that is preserving the functionality of caller
. If your constructor needs this (many do not), you can use Perl's magic goto &sub
syntax:
my $coderef = sub {unshift @_, 'MyModule::MyTool'; goto &{$_[0]->can('new')} };
That probably requires a little explanation. First, the module name is placed before any other arguments (which is what the new
method will expect). Then I used the UNIVERSAL
method ->can
to retrieve the coderef for the new
method. goto &{...}
then jumps to that coderef using the current argument list.
EDIT: The comments below show that there is some confusion as to when you would need to use the longer third technique. Here is a short segment that shows the problem:
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
So in short, Encapsulate::Better
's sub preserves the exact functionality of Original->new
whereas Encapsulate::Simple
permanently binds it to its package, breaking any encapsulated methods that use caller
for anything.
这篇关于如何获取构造函数的代码引用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!