2012-04-24 48 views
7

class_eval & instance_evaldef以外的工作方式有什麼區別嗎?內部class_evaldef塊定義方法的類本身(即實例方法)和內部instance_evaldef定義方法的類(即類方法)的eigenclass。 AFAIK所有其它特徵在兩種情況下(例如define_methodattr_accessorclass << self; end,定義常量)相同方式工作。這是真的嗎?class_eval vs instance_eval

答案是defundefalias有不同的上下文的class_evalinstance_eval

回答

13

長話短說:

  • (object = Object.new).instance_eval &block集:
  • Object.class_eval &block集:
    • selfObject
    • 「當前級」 到Object

「當前級」 被用於defundefalias,以及常數和類變量查找。


現在,讓我們來看一下實現細節。

下面是如何module_evalinstance_eval用C實現:

VALUE rb_mod_module_eval(int argc, VALUE *argv, VALUE mod) { 
    return specific_eval(argc, argv, mod, mod); 
} 

VALUE rb_obj_instance_eval(int argc, VALUE *argv, VALUE self) { 
    VALUE klass; 
    if (SPECIAL_CONST_P(self)) { klass = Qnil; } 
    else { klass = rb_singleton_class(self); } 
    return specific_eval(argc, argv, klass, self); 
} 

兩個呼叫specific_eval,它採用下列參數:int argcVALUE *argvVALUE klassVALUE self

注意的是:

  • module_eval經過ModuleClass實例作爲既klassself
  • instance_eval傳遞對象的singleton類作爲klass

如果給定的塊,specific_eval將呼叫yield_under,其中採用以下參數:VALUE under,VALUE selfVALUE values

if (rb_block_given_p()) { 
    rb_check_arity(argc, 0, 0); 
    return yield_under(klass, self, Qundef); 
} 

有在yield_under兩個重要行:

  1. block.self = self;

    此設置塊到接收器的self

  2. cref = vm_cref_push(th, under, NOEX_PUBLIC, blockptr);

    cref鏈表 指定的「當前級」,其用於defundefalias,以及 恆定和類變量查找。

    該行基本上將cref設置爲under

    最後:

    • 當從module_eval叫,under將是ClassModule 實例。

    • 當從instance_eval叫,under將是單例類的 self

+1

有一兩件事:內'class_eval'分配常量和類變量不起作用它在類定義的方式/重啓:它使用外部範圍。 – Alexey 2012-04-24 21:19:34

+0

@Alexey,你是對的。我敢打賭,這與'NODE_FL_CREF_PUSHED_BY_EVAL'常數有關。許多方法,例如['Module :: nesting'](http://ruby-doc.org/core-1.9.3/Module.html#method-c-nesting),似乎忽略了一個'cref'節點如果標誌被設置。 – 2012-04-24 22:38:06

0

instance_eval,您可以直接訪問實例的實例變量,並使用self作爲對實例的引用。

+1

'class_eval'並在這方面'instance_eval'工作相同 – Alexey 2012-04-24 19:11:45