Ruby的method_missing在其他语言中是否有等价物?

时间:2010-05-19 13:26:30

标签: programming-languages language-features method-missing

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

15 个答案:

答案 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__例如

在Python中实现
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

用脚注:

  • (17)firefox
  • (18)C#4,仅适用于“动态”物体

答案 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()事件处理程序将接收对该组件的未定义方法调用。参数missingMethodNamemissingMethodArguments会自动传入,允许动态处理丢失的方法调用。这种机制在开始构建各种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}}

已经有关于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