2010-05-19 14 views
21

W Ruby, obiekty mają poręczną metodę zwaną method_missing który pozwala na obsługiwanie połączeń metoda metod, które nie zostały nawet (wyraźnie) zdefiniowane:Czy istnieją odpowiedniki dla metody Ruby's method_missing w innych językach?

wywoływana przez Ruby gdy obj jest wysyłana wiadomość nie może obsłużyć. symbol jest symbolem dla wywoływanej metody, a argumenty są dowolnymi argumentami, które zostały do ​​niej przekazane. Domyślnie interpreter wywołuje błąd podczas wywoływania tej metody. Możliwe jest jednak zastąpienie tej metody, aby zapewnić bardziej dynamiczne zachowanie. Poniższy przykład tworzy klasę Roman, która odpowiada na metody o nazwach składających się z cyfr rzymskich, zwracając odpowiednie liczby całkowite.

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 

Na przykład, Ruby on Rails używa tego, aby umożliwić wywołań metod, takich jak find_by_my_column_name.

Moje pytanie brzmi, jakie inne języki obsługują odpowiednik method_missing i jak zaimplementować odpowiednik w swoim kodzie?

Odpowiedz

11

Niektóre przypadki użycia method_missing można zaimplementować w języku Python za pomocą __getattr__, np.

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

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

Następnie można zrobić:

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

'roman_to_int' ma pustą implementację, więc dlaczego r.iv zwraca 4? – Tim

+0

@ Tim Załóżmy, że wypełniamy puste pole kodem, który zwróci wartość. Po prostu postanowił nie pokazywać wszystkich szczegółów. Można to zrealizować za pomocą kalkulatora liczby rzymskiej na numer. –

12

Obiekty PHP mogą być przeciążone specjalną metodą __call.

Na przykład:

<?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 ma Proxy klasę, która może być rozszerzona do zapewnienia tej funkcji.

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 ma wiadomość doesNotUnderstand, który jest prawdopodobnie oryginalna realizacja tego pomysłu, ponieważ Smalltalk jest jednym z rodziców Ruby. Domyślna implementacja wyświetla okno błędu, ale można go przesłonić, aby zrobić coś bardziej interesującego.

+0

Cool! Ale czy jest jakiś przykład tego, w jaki sposób używałbyś tej wiadomości? –

+1

"W połączeniu z #become :, który powoduje, że dwa obiekty wymieniają miejsca w pamięci, ta możliwość #doesNotUnderstand: jest bardzo przydatna do implementacji obiektów ProxyObject, takich jakie są potrzebne w strukturach mapowania relacyjnych obiektów" http://c2.com/cgi/ wiki? CzyNotUnderstand –

5

W Common Lisp, no-applicable-method mogą być wykorzystywane do tego celu, zgodnie z Common Lisp Hyper Spec:

Funkcją generic no-zastosowanie-method jest wywoływana, gdy wywoływana jest funkcja ogólna i nie ma zastosowania żadna metoda tej funkcji ogólnej. Domyślna metoda sygnalizuje błąd.

Ogólna funkcja no-applicable-method nie jest przeznaczona do wywoływania przez programistów. Programiści mogą pisać na ten temat metody.

Tak na przykład:

(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

Nie sądzę, że daje to ten sam rodzaj cukru syntaktycznego, który dostajesz w Ruby. W przykładzie 'r.xxiii', aby wywołać NO-APPLICABLE-METHOD z (XXIII R), musisz najpierw zdefiniować funkcję generyczną o nazwie XXIII, prawda? –

2

Tcl ma coś podobnego. Za każdym razem, gdy wywołasz dowolne polecenie, którego nie można znaleźć, zostanie wywołana procedura unknown. Chociaż nie jest to coś, co zwykle używasz, może być czasem przydatny.

8

Perl ma AUTOLOAD, który działa na metodach klasy/obiektu podprogramów &.

Podprogram przykład:

use 5.012; 
use warnings; 

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

say foo(); # => FOO 

Przykład Klasa/Przedmiot wywołanie metody:

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

W CFML (ColdFusion Railo, OpenBD), uchwyt onMissingMethod() zdarzenie zdefiniowane w składniku, otrzyma niezdefiniowana metoda wzywa ten komponent. Argumenty missingMethodName i missingMethodArguments są automatycznie przekazywane, umożliwiając dynamiczną obsługę brakującego wywołania metody. Jest to mechanizm, który ułatwiał tworzenie niejawnych programów ustawiających/pobierających, zanim zaczęto je wbudowywać w różne silniki CFML.

9

JavaScript ma noSuchMethod, ale niestety jest to obsługiwane tylko przez Firefox/Spidermonkey.

Oto przykład:

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 ma IQuackFu - nie jest już doskonałe podsumowanie na SO w how-can-i-intercept-a-method-call-in-boo

Oto przykład:

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

szukałem to wcześniej, i znalazłem useful list (szybko wyprzedzany tutaj) jako część projektu Merd na 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 

W przypisach:

  • (17) firefox
  • (18) C# 4, tylko dla "dynamiczny" obiekty
5

C# ma teraz TryInvokeMember dla obiekty dynamiczne (dziedziczące po DynamicObject)

+4

DynamicObject ma nawet więcej metod pod tym względem, choć TryInvokeMember jest bezpośrednim odpowiednikiem metody method_missing. Istnieje również ExpandoObject, który pozwala dodawać właściwości w czasie wykonywania. W zależności od tego, co faktycznie chcesz rozwiązać, mogą to być również dobre sposoby. – OregonGhost

8

Dokonuje się tego w Lua, ustawiając klucz __index na metatable.

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

t.foo() 
t.bar() 

Wyjście kod:

foo 
bar 
2

Jego odpowiednik w Io jest przy użyciu metody forward.

Od docs:

Jeśli obiekt nie reaguje na wiadomość, będzie powoływać się na swój sposób „do przodu”, jeśli ma jeden ....

Oto prosty przykład:

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

Shout baz println  # => BAZ 

/I3az/