被method_missing委托的方法的arity是什么?

4

当我将类 A 的方法委托给 $delegation_target 实例,如下所示:

$delegation_target = ""
class A
  def method_missing *args, ≺ $delegation_target.send(*args, &pr) end
  def respond_to_missing? *args; $delegation_target.respond_to?(*args) end
end

A 实例的方法的 arity 返回 -1,而不管 $delegation_target 方法的 arity 如何。

def $delegation_target.foo; end
A.new.method(:foo).arity # => -1

def $delegation_target.foo arg1; end
A.new.method(:foo).arity # => -1

def $delegation_target.foo arg1, arg2; end
A.new.method(:foo).arity # => -1

def $delegation_target.foo arg1, arg2, arg3; end
A.new.method(:foo).arity # => -1

这个-1是从哪里来的?而且,有没有办法使得对于任何可能的方法名mA.new.method(m).arity返回$delegation_target.method(m)的元数(如果已定义)?
2个回答

6

Object#method以特殊的方式处理respond_to_missing?method_missing。让我们深入了解Ruby C源代码中发生了什么:

Object#method开始,我们调用mnew,它会为调用对象和传递的ID创建一个新的Method对象。在mnew的源代码中,当方法未定义时但respond_to_missing?返回true时,我们可以轻松地看到特殊处理:

if (UNDEFINED_METHOD_ENTRY_P(me)) {
    ID rmiss = rb_intern("respond_to_missing?");
    VALUE sym = ID2SYM(id);

    if (obj != Qundef && !rb_method_basic_definition_p(klass, rmiss)) {
        if (RTEST(rb_funcall(obj, rmiss, 2, sym, scope ? Qfalse : Qtrue))) {
            def = ALLOC(rb_method_definition_t);
            def->type = VM_METHOD_TYPE_MISSING;
            def->original_id = id;
            // ...
def->type = VM_METHOD_TYPE_MISSING;非常重要。查找VM_METHOD_TYPE_MISSING的定义,我们可以看到它是一个“method_missing(id)的包装器”。因此,实际上返回的方法只是method_missing,并且第一个参数已经指定为您最初尝试获取的方法的id。
通过验证method_missing的arity与我们得到的相同,我们可以确认我们的猜测:
A.new.method(:method_missing).arity  #=> -1

作为一种附带说明,-1 的元数意味着该方法可以接受无限数量的参数。
至于是否可以让它返回被调用方法的“真实”元数……不行。首先,Ruby 不会对您的 method_missing 发生什么做出任何假设,甚至不知道它只是委托给其他某些方法。

3

负数阶表示最后有一个以 * 开头的参数。如果有一个必需参数和一个可选附加数量的 * 参数(即一个必需参数和一个 * 参数),则其阶数表示为 -2,所以是 -n-1,其中 n 是必需参数的数量。

-n-1 被称为 n 的一补数,在 Ruby 中甚至有一个操作符可以获取必需参数的数量,即 ~ 操作符。

p ~A.new.method(:foo).arity #=> 0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接