【问题标题】:class_eval vs instance_evalclass_eval 与 instance_eval
【发布时间】:2012-05-05 08:08:32
【问题描述】:

除了defclass_evalinstance_eval 的工作方式有什么不同吗?在class_eval 块内部def 定义类本身的方法(即实例方法),在instance_eval def 内部定义类的特征类的方法(即类方法)。 AFAIK 所有其他功能在这两种情况下的工作方式相同(例如define_methodattr_accessorclass << self; end,定义常量)。是真的吗?

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

【问题讨论】:

    标签: ruby metaprogramming instance-eval class-eval


    【解决方案1】:

    长话短说:

    • Object.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 实例作为 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.

    【讨论】:

    • 有一件事:在class_eval 内部分配常量和类变量的工作方式与在类定义/重新打开时的工作方式不同:它使用外部范围。
    • @Alexey,你是对的。我敢打赌这与NODE_FL_CREF_PUSHED_BY_EVAL 常量有关。如果设置了标志,许多方法(例如 Module::nesting)似乎会忽略 cref 节点。
    • 第一个版本不应该是Object.class_eval &block吗?否则,您不会显示班级上的instance_eval 和班级上的class_eval 之间的区别,而是显示班级实例上的instance eval 和班级上的class_eval 之间的区别。
    • @MichaelHewson,我为class_eval 使用了一个类,因为该方法只为Module 实例定义。类是对象,因此 instance_eval 的行为相似,即使 object = Object 也是如此。 self 将是 Object 但块中定义的方法将成为 Object 的类方法,因为它们实际上是在 Object.singleton_class 上定义的。
    • @Michael,我明白你现在的意思了,我同意。我将编辑我的答案以反映这一点。
    【解决方案2】:

    instance_eval 让您可以直接访问实例的实例变量,并使用self 作为对实例的引用。

    【讨论】:

    • class_evalinstance_eval 在这方面的工作方式相同
    猜你喜欢
    • 2011-05-23
    • 2011-04-01
    • 2017-04-22
    • 1970-01-01
    • 2011-03-11
    • 2013-05-13
    • 1970-01-01
    • 2010-10-28
    • 2011-03-26
    相关资源
    最近更新 更多