其他编程语言中是否有类似于Ruby的method_missing方法的等价物?

23
在Ruby中,对象有一个方便的方法叫做method_missing,它允许处理甚至没有被(明确)定义的方法调用:
当对象收到无法处理的消息时,Ruby会调用这个方法。符号是调用的方法的符号,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个回答

16

Smalltalk有doesNotUnderstand消息,这可能是这个想法的最初实现,考虑到Smalltalk是Ruby的其中一个前身。默认实现会显示错误窗口,但可以被覆盖以执行更有趣的操作。


很酷!但是你有没有任何使用这个消息的示例呢? - Justin Ethier
2
结合 #become:,该功能使得两个对象在内存中交换位置。#doesNotUnderstand: 的这种能力非常有用,可用于实现 ProxyObjects,例如 Object Relational Mapping 框架所需的。http://c2.com/cgi/wiki?DoesNotUnderstand - Joe Koberg

12

使用 __call 特殊方法可以重载 PHP 对象。

例如:

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

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

roman_to_int 没有实现,那么为什么 r.iv 返回 4? - Tim
@Tim 你应该填写代码来返回一个值。他只是决定不显示所有细节。这可以通过罗马数字转换为数字实现。 - Christian Dean

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

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

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

8

在Lua中,这是通过设置元表__index键来实现的。

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

t.foo()
t.bar()

这段代码将输出:
foo
bar

8

Objective-C也支持类似的功能,并将其称为转发


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

3
我认为这不会给你在Ruby中获得的同样类型的语法糖。在 r.xxiii 的例子中,要从(XXIII R)调用NO-APPLICABLE-METHOD,你需要先定义一个名为XXIII的通用函数,是吗? - Kragen Javier Sitaker

5

4
DynamicObject在这方面甚至还有一些其他方法,虽然TryInvokeMember是method_missing的直接等价物。还有ExpandoObject,它允许在运行时添加属性。根据您实际想要解决的问题,这些也可能是不错的方式。 - OregonGhost

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接