如何获取构造函数的代码引用? [英] How to take code reference to constructor?

查看:44
本文介绍了如何获取构造函数的代码引用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码:

<前>我的 $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.

这篇关于如何获取构造函数的代码引用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆