Есть ли эквиваленты методу 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