【问题标题】:Are there equivalents to Ruby's method_missing in other languages?在其他语言中是否有 Ruby 的 method_missing 等价物?
【发布时间】:2011-02-21 09:25:19
【问题描述】:

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

【问题讨论】:

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


    【解决方案1】:

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

    【讨论】:

    • 酷!但是是否有任何地方可以说明您将如何使用此消息?
    • “与#become: 结合使用,它会导致两个对象在内存中交换位置,#doesNotUnderstand: 的这种功能对于实现对象关系映射框架中需要的代理对象非常有用”c2.com/cgi/wiki?DoesNotUnderstand
    【解决方案2】:

    可以使用__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');
    ?>
    

    【讨论】:

      【解决方案3】:

      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 您假设用返回值的代码填充空白。他只是决定不透露所有细节。这可以使用罗马数字到数字的转换器来实现。
      【解决方案4】:

      我之前一直在寻找这个,并在 SourceForge 上的 Merd 项目中找到了 useful list(很快就被取代了)。


       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,仅适用于“动态”对象

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          【解决方案6】:

          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
          

          【讨论】:

            【解决方案7】:

            Objective-C 支持同样的东西,并称之为forwarding

            【讨论】:

              【解决方案8】:

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

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

              这段代码将输出:

              foo
              bar
              

              【讨论】:

                【解决方案9】:

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

                【讨论】:

                • 我不认为这给了你在 Ruby 中得到的那种语法糖。在r.xxiii 示例中,要从 (XXIII R) 调用 NO-APPLICABLE-METHOD,您需要首先定义一个名为 XXIII 的通用函数,对吧?
                【解决方案10】:

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

                【讨论】:

                • DynamicObject 在这方面甚至还有更多的方法,尽管 TryInvokeMember 是 method_missing 的直接等价物。还有 ExpandoObject,它允许在运行时添加属性。根据您实际想要解决的问题,这些可能也是不错的方法。
                【解决方案11】:

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

                【讨论】:

                  【解决方案12】:

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

                  【讨论】:

                    【解决方案13】:

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

                    【讨论】:

                      【解决方案14】:

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

                      来自文档:

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

                      这是一个简单的例子:

                      Shout := Object clone do (
                          forward := method (
                              method_missing := call message name
                              method_missing asUppercase
                          )
                      )
                      
                      Shout baz println     # => BAZ
                      

                      /I3az/

                      【讨论】:

                        【解决方案15】:

                        Boo 有 IQuackFu - how-can-i-intercept-a-method-call-in-boo 已经有关于 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 
                        

                        【讨论】:

                          猜你喜欢
                          • 2014-02-24
                          • 1970-01-01
                          • 2011-11-30
                          • 2012-04-05
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2012-09-19
                          • 2012-05-20
                          相关资源
                          最近更新 更多