【问题标题】:How can I get a reference to a method?如何获得对方法的引用?
【发布时间】:2010-10-03 20:44:38
【问题描述】:

是否可以在 Ruby 中获取对对象方法的引用(我想知道这是否可以在没有 procs/lambdas 的情况下完成),例如,考虑以下代码:


class X
  def initialize
    @map = {}
    setup_map
  end

  private
  def setup_map
    # @map["a"] = get reference to a method
    # @map["b"] = get reference to b method
    # @map["c"] = get referebce to c method
  end

  public
  def call(a)
    @map["a"](a) if a > 10
    @map["b"](a) if a > 20
    @map["c"](a) if a > 30
  end

  def a(arg)
     puts "a was called with #{arg}"
  end

  def b(arg)
     puts "b was called with #{arg}"
  end

  def c(arg)
    puts "c was called with #{arg}"
  end
end

有可能做这样的事情吗?我想避免使用 procs/lambdas,因为我希望能够通过子类化来改变 A、B、C 的行为。

【问题讨论】:

    标签: ruby methods hash reference


    【解决方案1】:

    你想要Object#method:

    ---------------------------------------------------------- Object#method
         obj.method(sym)    => method
    ------------------------------------------------------------------------
         Looks up the named method as a receiver in obj, returning a Method 
         object (or raising NameError). The Method object acts as a closure 
         in obj's object instance, so instance variables and the value of 
         self remain available.
    
            class Demo
              def initialize(n)
                @iv = n
              end
              def hello()
                "Hello, @iv = #{@iv}"
              end
            end
    
            k = Demo.new(99)
            m = k.method(:hello)
            m.call   #=> "Hello, @iv = 99"
    
            l = Demo.new('Fred')
            m = l.method("hello")
            m.call   #=> "Hello, @iv = Fred"
    

    现在你的代码变成了:

    private
    def setup_map
      @map = {
        'a' => method(:a),
        'b' => method(:b),
        'c' => method(:c)
      }
      # or, more succinctly
      # @map = Hash.new { |_map,name| _map[name] = method(name.to_sym) }
    end
    
    public
    def call(arg)
      @map["a"][arg] if arg > 10
      @map["b"][arg] if arg > 20
      @map["c"][arg] if arg > 30
    end
    

    【讨论】:

      【解决方案2】:

      您可以使用 lambdas 做到这一点,同时保持更改子类行为的能力:

      class X
        def initialize
          @map = {}
          setup_map
        end
      
        private
        def setup_map
          @map["a"] = lambda { |a| a(a) }
          @map["b"] = lambda { |a| b(a) }
          @map["c"] = lambda { |a| c(a) }
        end
      
        public
        def call(a)
          @map["a"].call(a) if a > 10
          @map["b"].call(a) if a > 20
          @map["c"].call(a) if a > 30
        end
      
        def a(arg)
           puts "a was called with #{arg}"
        end
      
        def b(arg)
           puts "b was called with #{arg}"
        end
      
        def c(arg)
          puts "c was called with #{arg}"
        end
      end
      

      【讨论】:

      • 这行得通!但是,有没有办法获得对方法的实际引用?
      • 你能做的最好的就是 instance.methods ,它给出了一个字符串数组。
      【解决方案3】:

      Ruby 方法不是一流的对象;它通过消息传递实现OO。

      class X
        def call(a)
          self.send(:a, a) if a > 10
          self.send(:b, a) if a > 20
          self.send(:c, a) if a > 30
        end
      
        def a(arg)
           puts "a was called with #{arg}"
        end
      
        def b(arg)
           puts "b was called with #{arg}"
        end
      
        def c(arg)
          puts "c was called with #{arg}"
        end
      end
      

      或者直接打电话给他们:

      def call(a)
        self.a(a) if a > 10
        self.b(a) if a > 20
        self.c(a) if a > 30
      end
      

      【讨论】:

      • 这是一个简单的例子。对于我需要编写的类,我将有大约 20-30 种方法来检查。如果格子不是很时髦,对吧?
      • 我假设 if 语句是 switch 或 hash 查找的代理。 "send(:a, a)" 对我来说看起来比 "@map["a"][arg]" 更简单,如果有很多的话更是如此。
      【解决方案4】:

      您可以通过object.method(:method_name)获取对该方法的引用。

      例如:获取对system 方法的引用。

      m = self.method(:system)
      m.call('ls')
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-08-17
        • 2010-12-12
        • 2011-06-22
        • 1970-01-01
        • 2019-09-10
        • 1970-01-01
        相关资源
        最近更新 更多