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

Jus*_*ier 21 language-features programming-languages 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
Run Code Online (Sandbox Code Playgroud)

例如,Ruby on Rails使用它来允许调用诸如的方法find_by_my_column_name.

我的问题是,其他语言支持的等价物method_missing,以及如何在代码中实现等效语言?

Joh*_*ley 15

Smalltalk有doesNotUnderstand消息,这可能是这个想法的原始实现,因为Smalltalk是Ruby的父母之一.默认实现显示一个错误窗口,但可以覆盖它以执行更有趣的操作.

  • “与 #become: 相结合,这会导致两个对象在内存中交换位置,#doesNotUnderstand: 的这种功能对于实现 ProxyObjects 非常有用,例如对象关系映射框架中需要的”http://c2.com/cgi/wiki ?不明白 (2认同)

tyl*_*mac 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');
?>
Run Code Online (Sandbox Code Playgroud)


mik*_*kej 11

一些用例method_missing可以使用__getattr__例如Python在Python中实现

class Roman(object):
  def roman_to_int(self, roman):
    # implementation here

  def __getattr__(self, name):
    return self.roman_to_int(name)
Run Code Online (Sandbox Code Playgroud)

然后你可以这样做:

>>> r = Roman()
>>> r.iv
4
Run Code Online (Sandbox Code Playgroud)


muh*_*ten 9

Perl AUTOLOAD适用于子例程和类/对象方法.

子程序示例:

use 5.012;
use warnings;

sub AUTOLOAD {
    my $sub_missing = our $AUTOLOAD;
    $sub_missing =~ s/.*:://;
    uc $sub_missing;
}

say foo();   # => FOO
Run Code Online (Sandbox Code Playgroud)

类/对象方法调用示例:

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
Run Code Online (Sandbox Code Playgroud)


Jus*_*ier 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);
  }
}
Run Code Online (Sandbox Code Playgroud)


Edw*_*ale 8

Objective-C支持相同的事情并将其称为转发.


Mar*_*off 8

这是通过设置元表__index键在Lua中完成的.

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

t.foo()
t.bar()
Run Code Online (Sandbox Code Playgroud)

此代码将输出:

foo
bar
Run Code Online (Sandbox Code Playgroud)


Mat*_*hen 7

之前我一直在寻找这个,并在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
Run Code Online (Sandbox Code Playgroud)

用脚注:

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


Jus*_*ier 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))))
Run Code Online (Sandbox Code Playgroud)

  • 我不认为这会给你带来与Ruby相同的语法糖.在`r.xxiii`示例中,要从(XXIII R)调用NO-APPLICABLE-METHOD,您需要先定义一个名为XXIII的泛型函数,对吗? (2认同)

Mat*_*hen 5

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

  • DynamicObject甚至在这方面还有一些方法,尽管TryInvokeMember是method_missing的直接等价物.还有ExpandoObject,它允许在运行时添加属性.根据您实际想要解决的问题,这些也可能是好方法. (4认同)