使用method_missing委派的方法的Arity

时间:2012-12-09 17:34:17

标签: 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

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

-1

m来自何处?并且,有没有办法让它,以便,对于任何可能的方法名称A.new.method(m).arity$delegation_target.method(m)返回{{1}}的arity(如果已定义)?

2 个答案:

答案 0 :(得分:6)

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

Object#method开始,我们调用mnew,它会为调用它的对象创建一个新的Method对象并传递id。在mnew的来源中,我们可以很容易地看到未定义方法时的特殊处理,但respond_to_missing?在给定id时返回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;很重要。找到definition for VM_METHOD_TYPE_MISSING,我们发现它是“method_missing(id)”的“包装器”。所以返回的方法实际上只是method_missing,第一个参数已经被指定为你最初想要得到的方法的id。

我们可以通过验证method_missing的arity与我们获得的相同来证实我们的怀疑:

A.new.method(:method_missing).arity  #=> -1

顺便说一下,-1的arity意味着该方法可以采用无限数量的参数。

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

答案 1 :(得分:3)

负面arity意味着有一个最终的* -prefixed参数。如果有一个必需参数后跟一个* -prefixed参数(所以一个必需参数和一个可选的附加金额),那么arity将表示为-2,所以-n-1,其中n是所需参数的数量。

-n-1被称为n的一个补码,而ruby甚至有一个运算符来获取所需参数的数量,即~运算符。

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