2009-01-27 53 views
29

是否有可能在Ruby中获得一个对象的方法的引用(我想知道这是否可以在没有特效/ lambda表达式来完成),例如,考虑下面的代码:如何获得对某种方法的参考?


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的行为。

回答

42

你想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 
4

你可以用lambda表达式做到这一点,同时保持更改子类行为的能力:

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 
+0

This Works!但是,没有办法获得实际的方法参考吗? – Geo 2009-01-27 21:03:26

+0

你可以做的最好的是instance.methods,并给出了一个字符串数组。 – Samuel 2009-01-27 21:17:53

1

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 
+0

这是一个简单的例子。对于我需要编写的类,我将有大约20-30个方法来检查。如果支票不会很时尚,对吧? – Geo 2009-01-27 21:11:35

0

你可以得到的一个参考方法由object.method(:method_name)

例如:获得对system方法的参考。

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