Есть ли эквиваленты методу Ruby в других языках?

В Ruby у объектов есть удобный метод method_missing который позволяет обрабатывать вызовы методов для методов, которые даже не были (явно) определены:

Вызывается Ruby, когда obj отправляется сообщение, которое он не может обработать. Символ - это символ для вызываемого метода, а аргументы - любые аргументы, которые были переданы ему. По умолчанию интерпретатор выдает ошибку при вызове этого метода. Однако возможно переопределить метод, чтобы обеспечить более динамичное поведение. В приведенном ниже примере создается класс Roman, который отвечает на методы с именами, состоящими из римских цифр, и возвращает соответствующие целочисленные значения.

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

Например, Ruby on Rails использует это для разрешения вызовов таких методов, как find_by_my_column_name,

Мой вопрос в том, что другие языки поддерживают эквивалент method_missingи как вы реализуете эквивалент в своем коде?

15 ответов

Решение

Некоторые случаи использования method_missing может быть реализовано в Python с помощью __getattr__ например

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

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

Тогда вы можете сделать:

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

Smalltalk имеет doesNotUnderstand сообщение, которое, вероятно, является первоначальной реализацией этой идеи, учитывая, что Smalltalk является одним из родителей Руби. Реализация по умолчанию отображает окно ошибки, но оно может быть переопределено, чтобы сделать что-то более интересное.

Объекты PHP могут быть перегружены __call особый метод.

Например:

<?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');
?>

Я искал это раньше и нашел полезный список (здесь его быстро обгоняют) в рамках проекта Merd на 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

Со сносками:

  • (17) Firefox
  • (18) C# 4, только для "динамических" объектов

Perl имеет AUTOLOAD который работает с подпрограммами и методами класса / объекта.

Пример подпрограммы:

use 5.012;
use warnings;

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

say foo();   # => FOO

Пример вызова метода класса / объекта:

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

У JavaScript нет noSuchMethod, но, к сожалению, это поддерживается только Firefox/Spidermonkey.

Вот пример:

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);
  }
}

Objective-C поддерживает то же самое и называет это пересылкой.

Это достигается в Lua путем установки __index Ключ метатабельный.

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

t.foo()
t.bar()

Этот код выведет:

foo
bar

В C# теперь есть TryInvokeMember для динамических объектов (наследование от DynamicObject)

В Common Lisp, no-applicable-method может использоваться для этой цели, в соответствии с Common Lisp Hyper Spec:

Универсальная функция no-apply-method вызывается, когда вызывается универсальная функция, и никакой метод для этой универсальной функции не применим. Метод по умолчанию сообщает об ошибке.

Универсальная функция no-apply-method не предназначена для вызова программистами. Программисты могут написать методы для этого.

Так, например:

(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))))

Actionscript 3.0 имеет Proxy класс, который может быть расширен, чтобы обеспечить эту функциональность.

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

В CFML (ColdFusion, Railo, OpenBD) onMissingMethod() Обработчик событий, определенный в компоненте, будет получать неопределенные вызовы методов для этого компонента. Аргументы missingMethodName а также missingMethodArguments автоматически передаются, что позволяет динамически обрабатывать отсутствующий вызов метода. Это механизм, который облегчал создание неявных схем установщика / получателя до того, как они начали встраиваться в различные механизмы CFML.

Его эквивалент в Io использует forward метод.

Из документов:

Если объект не отвечает на сообщение, он вызовет свой метод "forward", если он есть...

Вот простой пример:

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

Shout baz println     # => BAZ

/ I3az /

У Tcl есть что-то похожее. Каждый раз, когда вы вызываете любую команду, которая не может быть найдена, будет вызвана неизвестная процедура. Хотя это не то, что вы обычно используете, иногда это может быть удобно.

Бу имеет IQuackFu - уже есть отличная сводка по SO в разделе " как можно перехватить метод вызова в бу"

Вот пример:

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 
Другие вопросы по тегам