Ruby 类评估与实例评估

Ruby 类评估与实例评估,ruby,metaprogramming,instance-eval,class-eval,Ruby,Metaprogramming,Instance Eval,Class Eval,除了def之外,class\u eval和instance\u eval的工作方式有什么不同吗?内部class_-evalblockdef定义了类自身的方法(即实例方法),内部instance_-eval定义了类的特征类的方法(即类方法)。AFAIK所有其他功能在这两种情况下都工作相同(例如,define_method,attr_accessor,classinstance_eval允许您直接访问实例的实例变量,并使用self作为实例的引用。长话短说: Object.instance\u ev

除了
def
之外,
class\u eval
instance\u eval
的工作方式有什么不同吗?内部
class_-eval
block
def
定义了类自身的方法(即实例方法),内部
instance_-eval
定义了类的特征类的方法(即类方法)。AFAIK所有其他功能在这两种情况下都工作相同(例如,
define_method
attr_accessor
class
instance_eval
允许您直接访问实例的实例变量,并使用
self
作为实例的引用。

长话短说:

  • Object.instance\u eval&block
    设置:
    • self
      to
      Object
    • 要创建的“当前类”
  • Object.class\u eval&block
    设置:
    • self
      to
      Object
    • 将“当前类”添加到
      对象
“当前类”用于
def
unde
alias
,以及常量和类变量查找


现在,让我们看一下实现细节

以下是如何在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);
}
这两个调用都采用以下参数:
int argc
VALUE*argv
VALUE klass
VALUE self

请注意:

  • module\u eval
    module
    Class
    实例作为
    klass
    self
  • instance\u eval
    将对象的单例类作为
    klass
如果给定一个块,
specific\u eval
将调用,它将使用以下参数:
下的值、
值自身
值值

if (rb_block_given_p()) {
    rb_check_arity(argc, 0, 0);
    return yield_under(klass, self, Qundef);
}
下的
yield\u中有两条重要的行:

  • block.self=self;

    这将块的
    自身设置为接收器

  • cref=vm\u cref\u push(th,under,NOEX\u PUBLIC,blockptr);

    这是一个链表 它指定了“当前类”,该类也用于
    def
    unde
    alias
    作为常量和类变量查找

    该行基本上将
    cref
    设置为
    下的

    最后:

    • module\u eval
      调用时,
    下的
    将是
    模块
    例如

  • instance\u eval
    调用时,
  • 下的
    将是
    
    self


    class\u eval
    instance\u eval
    在这方面的工作原理相同有一件事:在
    class\u eval
    内部分配常量和类变量的工作方式与在类定义/重新打开中的工作方式不同:它使用外部作用域。@Alexey,你是对的。我打赌这与
    NODE\u FL\u CREF\u有关_按_EVAL
    常量。例如,如果设置了标志,许多方法似乎会忽略
    cref
    节点。第一个版本不应该是
    Object.class\u EVAL&block
    ?否则,您不会显示类上的
    实例_EVAL
    和类上的
    类_EVAL
    之间的差异,而是显示了差异在类的实例上的
    instance eval
    和类上的
    class\u eval
    之间。@MichaelHewson,我为
    class\u eval
    使用了一个类,因为该方法仅为
    Module
    实例定义。类是对象,因此
    instance\u eval
    的行为类似,即使
    object=object
    >self
    将是
    对象
    ,但块中定义的方法将成为
    对象
    的类方法,因为它们实际上是在
    对象上定义的。singleton_class
    @Michael,我现在明白你的意思了,我同意。我将编辑我的答案以反映这一点。