2010-11-19 7 views
2

ich folgenden Code haben:Wie Code-Verweis auf Konstruktor nehmen?

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

aber wenn ich

 
$coderef->(); 

versuche ich bekam Fehler:

 
Not a CODE reference 

Wie kann ich Bezug auf Konstruktor übernehmen (ohne es zu telefonieren) und laufen Referenzcode spät?

Antwort

11

Der ${...} ist der Skalar-Dereferenzierungsoperator, nicht der anonyme Subroutinenkonstruktor. Sie wollen:

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

Und wenn Ihr Konstruktorargumente nimmt, könnte man es auf diese Weise schreiben:

my $coderef = sub {MyModule::MyTool->new(@_)}; 

Die beiden oben genannten Beispiele nicht ein Problem beheben, und dass die Funktionalität von caller wird erhalten. Wenn Ihr Konstruktor dies muss (viele nicht), können Sie Perl Magie goto &sub Syntax:

my $coderef = sub {unshift @_, 'MyModule::MyTool'; goto &{$_[0]->can('new')} }; 

, die wahrscheinlich eine kleine Erklärung erfordert. Zuerst wird der Modulname vor allen anderen Argumenten platziert (was die Methode new erwartet). Dann habe ich die UNIVERSAL Methode ->can verwendet, um die Coderef für die new Methode abzurufen. goto &{...} springt dann mit der aktuellen Argumentliste zu dieser Codeferenz.

EDIT: Die folgenden Kommentare zeigen, dass es einige Verwirrung gibt, wenn Sie die längere dritte Technik verwenden müssten. Hier ist ein kurzes Segment, das das Problem zeigt:

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 

Also kurz gesagt, Encapsulate::Better ‚s Unter bewahrt die genauen Funktionalität von Original->new während Encapsulate::Simple dauerhaft bindet es an die Verpackung, keine gekapselten Methoden zu brechen, die caller für alles verwenden.

+0

Aber ich möchte diesen Konstruktor nicht aufrufen. Ich brauche nur einen Hinweis darauf zu bekommen. Ich möchte ein Array von Codereferenzen für einige Konstruktoren erstellen, um sie später aufzurufen. – jesper

+0

@jesper, der anonyme Sub ruft den Konstruktor nicht auf, bis er mit '$ codeRef ->() ausgeführt wird;' – friedo

+0

Hmm, also muss ich mir meinen Code etwas genauer ansehen. ;) – jesper

1

Verwenden \& einen Verweis auf eine benannte Funktion zu erhalten:

my $coderef = \&MyModule::MyTool::new; 
+0

Aber wird diesen Code als' $ codeRef ->(); ' Übergeben Sie den Modulnamen als erstes Argument, da einige Konstruktoren erwarten können? – aschepler

+0

@aschepler - Nein, wird es nicht. Schauen Sie sich stattdessen die Antwort von Eric Strom an. – mob

+0

Ich denke, es ist nicht - deshalb funktioniert dieser Code nicht für mich. – jesper

-1

Dies sollte die neue Werk hält, unabhängig davon, welches Paket:

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

So dass, wenn MyModule::MyTool nicht ihren Konstruktor nicht implementieren, Sie können immer noch es Griff bekommen.

+2

Das übergibt den Klassennamen immer noch nicht, wenn Sie '$ coderef ->()' verwenden. – cjm

+0

@cjm: Einverstanden, aber es ist 1) ein codref zum Konstruktor und 2) es wird sehr wahrscheinlich nicht "Nicht eine CODE-Referenz" bekommen. Es spricht das angegebene Problem an. Ich denke, er würde schnell lernen, dass er eine Schließung statt der eigentlichen Referenz will. – Axeman

+1

Ist das Problem nicht, dass Leute versuchen, eine Methref auf die LHS des Aufrufpfeils statt auf die RHS zu setzen, wo sie hingehört? Tun Sie nicht '$ coderef -> (args)', tun Sie 'invocant -> $ coderef (args) '. – tchrist

Verwandte Themen