class_eval vs instance_eval
Есть ли разница в том, как class_eval
& instance_eval
работать кроме def
? внутри class_eval
блок def
определяет метод для самого класса (т.е. метод экземпляра) и внутри instance_eval
def
определяет метод для собственного класса класса (т. е. метод класса). 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
:
block.self = self;
Это устанавливает
self
блока к получателю.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
как ссылка на экземпляр.