2012-04-24 19 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