22

在 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,以及如何在代码中实现等价物?

4

15 回答 15

16

Smalltalk 有doesNotUnderstand消息,这可能是这个想法的原始实现,因为 Smalltalk 是 Ruby 的父母之一。默认实现显示一个错误窗口,但它可以被覆盖以做一些更有趣的事情。

于 2010-05-19T13:36:40.073 回答
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');
?>
于 2010-05-19T13:30:48.030 回答
11

的一些用例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
于 2010-05-19T13:44:00.243 回答
10

我之前一直在寻找这个,并在 SourceForge 上的 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

带脚注:

  • (17) 火狐
  • (18) C# 4,仅适用于“动态”对象
于 2010-05-20T02:52:59.653 回答
9

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
于 2010-05-19T14:13:12.760 回答
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);
  }
}
于 2010-05-20T02:36:53.680 回答
8

Objective-C 支持同样的事情并称之为forwarding

于 2010-05-19T13:27:55.287 回答
8

这在 Lua 中通过设置metatable__index的键来完成。

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

t.foo()
t.bar()

此代码将输出:

foo
bar
于 2010-05-20T03:15:04.240 回答
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))))
于 2010-05-19T14:08:02.893 回答
5

C# 现在有TryInvokeMember,用于动态对象(继承自DynamicObject

于 2010-05-20T02:55:31.083 回答
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
    }
}  
于 2010-05-19T13:32:46.890 回答
2

Tcl 有类似的东西。每当您调用任何找不到的命令时,都会调用过程unknown。虽然它不是您通常使用的东西,但它有时会很方便。

于 2010-05-19T14:12:31.150 回答
2

在 CFML(ColdFusion、Railo、OpenBD)中,onMissingMethod()在组件中定义的事件处理程序将接收该组件上未定义的方法调用。参数missingMethodNamemissingMethodArguments自动传入,允许动态处理丢失的方法调用。在隐式 setter/getter 方案开始内置到各种 CFML 引擎之前,这种机制有助于创建它们。

于 2010-05-20T01:55:38.967 回答
2

它的等价物Io是使用forward方法。

从文档:

如果一个对象没有响应消息,它会调用它的“forward”方法,如果它有一个......

这是一个简单的例子:

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

Shout baz println     # => BAZ

/I3az/

于 2011-05-12T19:07:10.140 回答
1

Boo 有- 在how-can-i-intercept-a-method-call-in-booIQuackFu已经有关于 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 
于 2010-05-20T02:49:22.110 回答