class_eval vs instance_eval

时间:2012-04-24 16:34:57

标签: ruby metaprogramming instance-eval class-eval

class_eval&amp;除instance_eval以外的def工作?内部class_evaldef定义类本身的方法(即实例方法),内部instance_eval def定义类的本征类的方法(即类方法)。 AFAIK所有其他功能在两种情况下都相同(例如define_methodattr_accessorclass << self; end,定义常量)。这是真的吗?

答案defundefalias针对class_evalinstance_eval设置了不同的上下文。

2 个答案:

答案 0 :(得分:13)

长话短说:

  • (object = Object.new).instance_eval &block设置:
  • Object.class_eval &block设置:
    • selfObject
    • Object
    • 的“当前课程”

“当前类”用于defundefalias,以及常量和类变量查找。


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

以下是在C:

中实现module_evalinstance_eval的方式
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_evalModuleClass个实例作为klass self
  • 传递
  • instance_eval将对象的单例类传递为klass

如果有一个阻止,specific_eval会调用yield_under,其中包含以下参数:VALUE underVALUE 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 :(得分:0)

instance_eval允许您直接访问实例的实例变量,并使用self作为实例的引用。