2010-05-19 10 views
21

In Ruby, gli oggetti hanno un metodo pratico chiamato method_missing che permette di gestire le chiamate di metodo per i metodi che non sono nemmeno stati (esplicitamente) definito:Esistono equivalenti a method_missing di Ruby in altre lingue?

Invocato da Ruby quando obj viene inviato un messaggio che non può gestire. symbol è il simbolo per il metodo chiamato, e args sono tutti gli argomenti che sono stati passati ad esso. Per impostazione predefinita, l'interprete genera un errore quando viene chiamato questo metodo. Tuttavia, è possibile sovrascrivere il metodo per fornire un comportamento più dinamico. L'esempio seguente crea una classe Roman, che risponde ai metodi con nomi costituiti da numeri romani, restituendo i corrispondenti valori interi.

class Roman 
def romanToInt(str) 
    # ... 
end 
def method_missing(methId) 
    str = methId.id2name 
    romanToInt(str) 
end 
end 

r = Roman.new 
r.iv  #=> 4 
r.xxiii #=> 23 
r.mm  #=> 2000 

Ad esempio, rubino sulle rotaie utilizza questo per consentire le chiamate a metodi come find_by_my_column_name.

La mia domanda è, quali altre lingue supportano un equivalente a method_missing e come si implementa l'equivalente nel codice?

risposta

11

Alcuni casi d'uso di method_missing possono essere implementati in Python utilizzando __getattr__ ad es.

class Roman(object): 
    def roman_to_int(self, roman): 
    # implementation here 

    def __getattr__(self, name): 
    return self.roman_to_int(name) 

Poi si può fare:

>>> r = Roman() 
>>> r.iv 
4 
+0

'roman_to_int' ha un'implementazione vuota, quindi perché r.iv restituisce 4? – Tim

+0

@Tim Supponiamo di riempire lo spazio vuoto con il codice che restituirebbe un valore. Ha appena deciso di non mostrare tutti i dettagli. Questo potrebbe essere implementato con un numero romano al convertitore di numeri. –

8

Objective-C supporta la stessa cosa e lo chiama forwarding.

12

Gli oggetti PHP possono essere sovraccaricati con il metodo speciale __call.

Ad esempio:

<?php 
class MethodTest { 
    public function __call($name, $arguments) { 
     // Note: value of $name is case sensitive. 
     echo "Calling object method '$name' " 
      . implode(', ', $arguments). "\n"; 
    } 
} 

$obj = new MethodTest; 
$obj->runTest('in object context'); 
?> 
2

Actionscript 3.0 ha una classe Proxy che può essere esteso per fornire questa funzionalità.

dynamic class MyProxy extends Proxy { 
    flash_proxy override function callProperty(name:*, ...rest):* { 
    try { 
     // custom code here 
    } 
    catch (e:Error) { 
     // respond to error here 
    } 
} 
15

Smalltalk ha il messaggio doesNotUnderstand, che è probabilmente l'implementazione originale di questa idea, visto che Smalltalk è uno dei genitori di Ruby. L'implementazione di default mostra una finestra di errore, ma può essere sovrascritta per fare qualcosa di più interessante.

+0

Cool! Ma c'è un esempio di come useresti questo messaggio? –

+1

"Combinato con #become :, che fa scambiare due oggetti in memoria, questa capacità di #doesNotUnderstand: è molto utile per implementare ProxyObjects come sono necessari nei framework di mappatura relazionale degli oggetti" http://c2.com/cgi/ wiki? DoesNotUnderstand –

5

In Common Lisp, no-applicable-method possono essere utilizzati per questo scopo, secondo il Common Lisp Hyper Spec:

La funzione generica non-applicabile-metodo viene chiamato quando viene invocata una funzione generica e non è applicabile alcun metodo su quella funzione generica. Il metodo predefinito segnala un errore.

La funzione generica no-applicable-method non è progettata per essere chiamata dai programmatori. I programmatori possono scrivere metodi per questo.

Così, per esempio:

(defmethod no-applicable-method (gf &rest args) 
    ;(error "No applicable method for args:~% ~s~% to ~s" args gf) 
    (%error (make-condition 'no-applicable-method :generic-function gf :arguments args) '() 
     ;; Go past the anonymous frame to the frame for the caller of the generic function 
     (parent-frame (%get-frame-ptr)))) 
+2

Non penso che questo ti dia lo stesso tipo di zucchero sintattico che ottieni da Ruby, comunque. Nell'esempio 'r.xxiii', per invocare NO-APPLICABLE-METHOD da (XXIII R), dovresti prima definire una funzione generica chiamata XXIII, giusto? –

2

Tcl ha qualcosa di simile. Ogni volta che si chiama un comando che non può essere trovato, verrà chiamata la procedura unknown. Anche se non è qualcosa che normalmente usi, a volte può essere utile.

8

Perl ha AUTOLOAD che funziona su subroutine & metodi classe/oggetto.

esempio sottoprogrammi:

use 5.012; 
use warnings; 

sub AUTOLOAD { 
    my $sub_missing = our $AUTOLOAD; 
    $sub_missing =~ s/.*:://; 
    uc $sub_missing; 
} 

say foo(); # => FOO 

esempio/metodo Oggetto chiamata Classe:

use 5.012; 
use warnings; 

{ 
    package Shout; 

    sub new { bless {}, shift } 

    sub AUTOLOAD { 
     my $method_missing = our $AUTOLOAD; 
     $method_missing =~ s/.*:://; 
     uc $method_missing; 
    } 
} 

say Shout->bar;   # => BAR 

my $shout = Shout->new; 
say $shout->baz;  # => BAZ 
2

In CFML (ColdFusion, Railo, OpenBD), il gestore onMissingMethod() evento, definito all'interno di un componente, riceveranno chiamate di metodo non definite su quel componente. Gli argomenti missingMethodName e missingMethodArguments vengono inoltrati automaticamente, consentendo la gestione dinamica della chiamata del metodo mancante. Questo è il meccanismo che ha facilitato la creazione di schemi impliciti setter/getter prima che iniziassero a essere incorporati nei vari motori CFML.

9

JavaScript ha noSuchMethod, ma sfortunatamente questo è supportato solo da Firefox/Spidermonkey.

Ecco un esempio:

wittyProjectName.__noSuchMethod__ = function __noSuchMethod__ (id, args) { 
    if (id == 'errorize') { 
    wittyProjectName.log("wittyProjectName.errorize has been deprecated.\n" + 
         "Use wittyProjectName.log(message, " + 
         "wittyProjectName.LOGTYPE_ERROR) instead.", 
         this.LOGTYPE_LOG); 
    // just act as a wrapper for the newer log method 
    args.push(this.LOGTYPE_ERROR); 
    this.log.apply(this, args); 
    } 
} 
1

Boo ha IQuackFu - c'è già un eccellente sintesi su SO how-can-i-intercept-a-method-call-in-boo

Ecco un esempio:

class XmlObject(IQuackFu): 
_element as XmlElement 

def constructor(element as XmlElement): 
    _element = element 

def QuackInvoke(name as string, args as (object)) as object: 
    pass # ignored 

def QuackSet(name as string, parameters as (object), value) as object: 
    pass # ignored 

def QuackGet(name as string, parameters as (object)) as object: 
    elements = _element.SelectNodes(name) 
    if elements is not null: 
     return XmlObject(elements[0]) if elements.Count == 1 
     return XmlObject(e) for e as XmlElement in elements 

override def ToString(): 
    return _element.InnerText 
7

che cercavo questo prima, e ha trovato un useful list (rapidamente superato da qui) come parte del progetto Merd su SourceForge.


Construct       Language 
-----------      ---------- 
AUTOLOAD       Perl 
AUTOSCALAR, AUTOMETH, AUTOLOAD... Perl6 
__getattr__      Python 
method_missing      Ruby 
doesNotUnderstand     Smalltalk 
__noSuchMethod__(17)    CoffeeScript, JavaScript 
unknown       Tcl 
no-applicable-method    Common Lisp 
doesNotRecognizeSelector   Objective-C 
TryInvokeMember(18)    C# 
match [name, args] { ... }   E 
the predicate fail     Prolog 
forward       Io 

Con note a piè pagina:

  • (17) di Firefox
  • (18) C# 4, solo per "dinamica" oggetti
5

C# ha ora TryInvokeMember, per oggetti dinamici (che ereditano da DynamicObject)

+4

DynamicObject ha anche qualche altro metodo a riguardo, sebbene TryInvokeMember sia l'equivalente diretto di method_missing. E c'è anche ExpandoObject, che consente di aggiungere proprietà in fase di runtime. A seconda di ciò che vuoi veramente risolvere, anche questi possono essere buoni modi. – OregonGhost

8

Questo viene eseguito in Lua impostando la chiave __index di metatable.

t = {} 
meta = {__index = function(_, idx) return function() print(idx) end end} 
setmetatable(t, meta) 

t.foo() 
t.bar() 

Questa uscita codice:

foo 
bar 
2

suo equivalente in Io sta utilizzando il metodo forward.

Dalla documentazione:

Se un oggetto non risponde a un messaggio, si invocherà il suo metodo "avanti" se ne ha una ....

Ecco un semplice esempio:

Shout := Object clone do (
    forward := method (
     method_missing := call message name 
     method_missing asUppercase 
    ) 
) 

Shout baz println  # => BAZ 

/I3az/