Как взять код ссылки на конструктор? - PullRequest
2 голосов
/ 20 ноября 2010

У меня есть следующий код:

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

, но когда я пытаюсь

$coderef->();

я получаю сообщение об ошибке:

Not a CODE reference

Как я могу взять ссылку на конструктор(не вызывая его) и поздно запустить ссылочный код?

Ответы [ 3 ]

11 голосов
/ 20 ноября 2010

${...} - это скалярный оператор разыменования, а не анонимный конструктор подпрограмм. Вы хотите:

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, чтобы получить coderef для метода new. goto &{...} затем переходит на этот код, используя текущий список аргументов.

РЕДАКТИРОВАТЬ: комментарии ниже показывают, что есть некоторая путаница относительно того, когда вам нужно будет использовать более длинный третий метод. Вот короткий сегмент, который показывает проблему:

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 голос
/ 20 ноября 2010

Используйте \&, чтобы получить ссылку на именованную функцию:

my $coderef = \&MyModule::MyTool::new;
0 голосов
/ 20 ноября 2010

Это должно работать независимо от того, какой пакет содержит новый:

my $coderef = MyModule::MyTool->UNIVERSAL::can( 'new' ); 

Так что если MyModule::MyTool не реализует их конструктор, вы все равно можете получить его дескриптор.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...