class_eval vs instance_eval

Есть ли разница в том, как class_eval & instance_eval работать кроме def? внутри class_eval блок def определяет метод для самого класса (т.е. метод экземпляра) и внутри instance_evaldef определяет метод для собственного класса класса (т. е. метод класса). AFAIK все остальные функции работают одинаково в обоих случаях (например, define_method, attr_accessor, class << self; end, определяя константы). Это правда?

Ответ: def, undef а также alias иметь разные контексты для class_eval а также instance_eval,

2 ответа

Решение

Короче:

  • (object = Object.new).instance_eval &block наборы:
    • self в object
    • "Текущий класс" для object.singleton_class
  • Object.class_eval &block наборы:
    • self в Object
    • "Текущий класс" для 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);
}

Оба зовут specific_eval, который принимает следующие аргументы: int argc, VALUE *argv, VALUE klass а также VALUE self,

Обратите внимание, что:

  • module_eval проходит Module или же Class экземпляр как оба klass а также self
  • instance_eval передает синглтон-класс объекта как klass

Если дано блок, specific_eval позвоню yield_under, который принимает следующие аргументы: VALUE under, VALUE self а также VALUE 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 является связанным списком, который определяет "текущий класс", который используется для def, undef а также alias, а также поиск констант и переменных класса.

    Эта линия в основном устанавливает cref в under,

    В заключение:

    • Когда звонят из module_eval, under будет Class или же Module пример.

    • Когда звонят из instance_eval, under будет синглтон класс self,

instance_eval позволяет напрямую обращаться к переменным экземпляра экземпляра и использовать self как ссылка на экземпляр.

Другие вопросы по тегам