我正在研究Ruby Koans的about_message_passing.rb,并使代码适用于method_missing,如下所示:
def method_missing(method_name, *args, &block)
@messages << method_name
@object.__send__(method_name, *args, &block)
end
这段代码似乎有效,但我不太明白为什么需要* args和&amp;块需要。
如果我定义了一种方法,我理解*和&amp;用于分别表示数组参数和块参数,但是当它们与send方法一起使用以调用对象上的方法时它意味着什么?
答案 0 :(得分:9)
我会一次拿这些。完全取消method_missing
,因为它只是令人困惑。这实际上与此完全无关。
splat *
做两件事。在方法 definition 的参数中,它将多个参数吸收到数组中。在方法调用中使用时,它会将数组展开为单个参数。使用两者允许您将任意数量的参数转发给另一个方法。
def foo(*args)
bar(*args)
end
def bar(a, b, c)
puts a
puts b
puts c
end
foo(1,2,3) # prints 1, 2 and then 3
由于您基本上是转发所有参数,因此这是相同的模式。
&
用于块参数。每个方法调用中可以只有一个,它是挂起的块。这是一个特殊的论点,因为它不直接参与论证。您可以通过捕获添加&someblock
作为方法定义中的最后一个参数来将块捕获到变量。
然后,您可以使用相同的语法在方法调用中传递一个块。
def foo(&block)
bar(&block)
end
def bar
yield
end
foo { puts 'hello' } # prints hello
这允许您将挂起块传递给另一个方法,而无需调用它。它并不总是必需的,因为您通常只使用yield
来执行传递的任何块。但是如果除了执行它之外你还想做一些事情,你需要捕获对块本身的引用。
因此,如果你将这两件事结合起来,你将获得最终的方法转发器。您捕获所有任意数量的参数,以及任何悬而未决的块,并将其发送到另一个方法。
# forwards everything to the method `bar`
def foo(*args, &block)
bar(*args, &block)
end
最后,send
只是一种方法。它需要一个方法的名称,后跟任意数量的参数(不是数组),并且可以选择处理挂起块。
换句话说:
foo.send methodName, *args, &block
答案 1 :(得分:1)
方法定义中的splat意味着“获取所有不匹配的参数并将它们放在一个数组中”(在ruby 1.8中,这始终是最后一个参数,但1.9中的splats可能出现在中间)。
在方法调用中使用它是相反的:它意味着获取此数组并将其内容用作参数
foo(a,b) #call foo with 2 arguments: a and b
foo([a,b]) #call foo with a single array argument
foo(*[a,b]) # call foo with 2 arguments: a and b
&安培;类似于:在方法定义中它捕获块并将其转换为proc,但在方法调用中,它将proc(或proc类似对象 - 响应to_proc
的任何内容将转换为该方法的块)
method_missing
需要这两个,因为(通常)你想传递原始方法调用中的所有参数和块。
答案 2 :(得分:0)
据我所知,无论何时直接传递一个块,都会使用语法&amp; block_name。
此外,Object#send的方法签名需要无限的参数,而不是数组。因此,通过传递splatted值* args,它就像你传递逗号分隔的args一样。