使用method_missing委派的方法的Arity

saw*_*awa 3 ruby delegation method-missing arity

当我将类的实例上的方法委托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
Run Code Online (Sandbox Code Playgroud)

arity对实例的方法A回报-1,而不管arity对方法$delegation_target:

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
Run Code Online (Sandbox Code Playgroud)

-1是从哪里来的?并且,有没有办法使它,以便,对于任何可能的方法名称m,A.new.method(m).arity返回$delegation_target.method(m)(如果它被定义)的arity ?

And*_*all 6

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

Object#method我们调用开始mnew,它Method为被调用的对象创建一个新对象并传递id.在源代码中,mnew我们可以很容易地看到未定义方法时的特殊处理,但在给定id时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;
            // ...
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

顺便-1说一句,这意味着该方法可以采用无限数量的参数.

至于你是否可以让它返回被称为方法的"真实"方面......不,你不能.首先,Ruby不会对你所发生的事情做出任何假设method_missing,甚至不知道它只是委托给其他方法.