How to take code reference to constructor?
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.
Use \&
to obtain a reference to a named function:
my $coderef = \&MyModule::MyTool::new;
This should work regardless of which package holds the new:
my $coderef = MyModule::MyTool->UNIVERSAL::can( 'new' );
So that if MyModule::MyTool
does not implement their constructor, you can still get it's handle.
精彩评论