2012-12-09 26 views
2

委派當我委派上A類的實例方法來$delegation_target如下:關於A返回-1實例元數法的以method_missing的

$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不論方法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)(如果已定義)的元數據?

回答

5

Object#method以特殊的方式處理respond_to_missing? & method_missing。讓我們潛入Ruby的C源代碼,看看會發生什麼:

始於Object#method,我們稱之爲mnew,這對於它的呼籲對象,並通過ID創建一個新的Method對象。在源mnew,我們可以很容易地看到特殊處理沒有定義的方法的時候,但respond_to_missing?回報true給出的ID時:

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的元數是一樣的,我們在說什麼確認了我們的懷疑:

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

順便說一句,的-1的元數意味着該方法可以採取不限數量的論據。

至於是否可以讓它返回被調用方法的「真實」元組...不,你不能。首先,Ruby不會假設你的method_missing會發生什麼,甚至不知道它只是委託給其他方法。

2

負的arity意味着有一個最終的* -prefixed參數。如果有一個必需的參數後跟一個*前綴(因此需要一個參數和一個可選的附加量),那麼arity將表示爲-2,所以-n-1,其中n是所需參數的數量。

-n-1被稱爲n的補碼,而ruby甚至有一個運算符來獲取所需參數的個數,即~運算符。

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