class_eval vs instance_eval

Ale*_*xey 7 ruby metaprogramming instance-eval class-eval

除了工作class_evalinstance_eval工作有什么不同def吗?内部class_evaldef定义了类本身的方法(即实例方法),内部instance_eval def定义了类的本征类的方法(即类方法).据我所知其他所有功能在这两种情况下(例如相同的工作define_method,attr_accessor,class << self; end,定义常量).这是真的吗?

答案是:def,undef并且alias有不同的背景class_evalinstance_eval.

Mat*_*ira 13

长话短说:

  • Object.instance_eval &block 集:
  • Object.class_eval &block 集:
    • selfObject
    • "现在的班级"来 Object

"当前级"被用于def,undef并且alias,以及常数和类变量查找.


现在,我们来看看实现细节.

以下是如何module_eval以及instance_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);
}
Run Code Online (Sandbox Code Playgroud)

这两个电话specific_eval,其中采用下列参数:int argc,VALUE *argv,VALUE klassVALUE self.

注意:

  • module_eval传递ModuleClass实例作为klass self
  • instance_eval穿过对象的单例类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);
}
Run Code Online (Sandbox Code Playgroud)

有两条重要的路线yield_under:

  1. block.self = self;

    这将self块设置为接收器.

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

    cref是一个链表 ,其指定了"当前级",其用于def,undefalias,以及常数和类变量查找.

    该线基本上设置crefunder.

    最后:

    • 从中调用时module_eval,under将是ClassModule 实例.

    • 当从所谓的instance_eval,under将是单例类self.