在Ruby中,对象有一个名为method_missing
的方便方法,它允许人们处理甚至没有(显式)定义的方法的方法调用:
当obj被发送一条无法处理的消息时,由Ruby调用。 symbol是被调用方法的符号,args是传递给它的任何参数。默认情况下,解释器在调用此方法时会引发错误。但是,可以覆盖该方法以提供更多动态行为。下面的示例创建一个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
,以及如何在代码中实现等效语言?
答案 0 :(得分:15)
Smalltalk有doesNotUnderstand
消息,这可能是这个想法的最初实现,因为Smalltalk是Ruby的父母之一。默认实现显示一个错误窗口,但可以覆盖它以执行更有趣的操作。
答案 1 :(得分:12)
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');
?>
答案 2 :(得分:11)
method_missing
的一些用例可以使用__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
答案 3 :(得分:9)
Perl的AUTOLOAD
适用于子程序&amp;类/对象方法。
子程序示例:
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
答案 4 :(得分:9)
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);
}
}
答案 5 :(得分:8)
Objective-C支持同样的事情并将其称为forwarding。
答案 6 :(得分:8)
这是通过设置metatable的__index
密钥在Lua中完成的。
t = {}
meta = {__index = function(_, idx) return function() print(idx) end end}
setmetatable(t, meta)
t.foo()
t.bar()
此代码将输出:
foo
bar
答案 7 :(得分:7)
之前我一直在寻找这个,并在SourceForge上找到了useful list(很快就被超越)作为Merd项目的一部分。
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
用脚注:
答案 8 :(得分:5)
在Common Lisp中,no-applicable-method
可用于此目的,根据Common Lisp Hyper Spec:
当调用泛型函数并且该泛型函数上没有方法适用时,将调用泛型函数no-applicable-method。默认方法表示错误。
通用函数no-applicable-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))))
答案 9 :(得分:5)
C#现在有TryInvokeMember,用于动态对象(继承自DynamicObject)
答案 10 :(得分:3)
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
}
}
答案 11 :(得分:2)
Tcl有类似的东西。每当您调用任何无法找到的命令时,都会调用过程unknown。虽然它不是您通常使用的东西,但它有时会很方便。
答案 12 :(得分:2)
在CFML(ColdFusion,Railo,OpenBD)中,组件中定义的onMissingMethod()
事件处理程序将接收对该组件的未定义方法调用。参数missingMethodName
和missingMethodArguments
会自动传入,允许动态处理丢失的方法调用。这种机制在开始构建各种CFML引擎之前便于创建隐式setter / getter方案。
答案 13 :(得分:2)
它在Io
中的等价物使用forward
方法。
来自文档:
如果一个对象没有响应一条消息,它会调用它的“前向”方法,如果有一个....
这是一个简单的例子:
Shout := Object clone do (
forward := method (
method_missing := call message name
method_missing asUppercase
)
)
Shout baz println # => BAZ
/ I3az /
答案 14 :(得分:1)
Boo有IQuackFu
- 在[{3}}
以下是一个例子:
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