instance_eval
и class_eval
позволяют выполнить фрагмент кода. Так что вы можете сказать? Старомодный eval
может сделать это. Но instance_eval
и class_eval
принимают аргумент блока для фрагмента кода. Таким образом, фрагмент кода не обязательно должен быть строкой. Также instance_eval
и class_eval
позволяют приемник (в отличие от старого eval
). Следовательно, вы можете вызывать эти два современных метода для объекта класса или даже для объекта экземпляра.
class A
end
A.instance_eval do
# self refers to the A class object
self
end
a = A.new
a.instance_eval do
# self refers to the a instance object
self
end
Также помните, что в ruby, если мы вызываем метод без получателя, метод будет вызываться на self
, который в блоке instance_eval
является объектом, на который мы вызвали instance_eval
. Переменные экземпляра являются частными в ruby. Вы не можете получить к ним доступ вне класса, в котором они определены. Но поскольку переменные экземпляра хранятся в self
, мы можем получить к ним доступ в instance_eval
(то же самое относится и к закрытым методам, которые нельзя вызвать с помощью получателя):
class A
def initialzie
@a = “a”
end
private
def private_a
puts “private a”
end
end
a = A.new
puts a.instance_eval { @a }
# => “a”
puts a.instance_eval { private_a }
# => “private a”
Мы также можем добавить методы к получателю в instance_eval
и class_eval
. Здесь мы добавляем его к instance_eval
:
class A
end
A.instance_eval do
def a_method
puts “a method”
end
end
A.a_method
# => a method
Теперь подумайте, что мы только что сделали. Мы использовали instance_eval
, определили метод в его block
, а затем вызвали метод для самого объекта класса. Разве это не метод класса? Думайте об этом как о методе «класса», если хотите. Но все, что мы сделали, это определили метод для получателя в блоке instance_eval
, и получатель оказался A
. Мы можем легко сделать то же самое для объекта экземпляра:
a.instance_eval do
def a_method
puts "a method"
end
end
a.a_method
# => a method
И работает точно так же. Не думайте о методах класса как о методах класса в других языках. Это просто методы, определенные в self
, когда self
оказывается объектом класса (начиная с Class.new
, как в class A end
).
Но я хочу взять этот ответ немного глубже, чем принятый ответ. Где instance_eval
на самом деле придерживается методов, которые вы помещаете в них? Они входят в класс singleton
приемника! Как только вы вызываете instance_eval
на приемнике, интерпретатор ruby открывает singleton_class
и помещает методы, определенные в блоке, в этот singleton_class
. Это похоже на использование extend
в классе (так как extends открывает одноэлементный класс и помещает методы в модуль, переданный для расширения в одноэлементный класс)! Он открывает singleton_class
, который является частью иерархии наследования (прямо перед родительским классом): A -> singleton_class -> Parent
Теперь, что отличает class_eval
? class_eval
можно вызывать только для классов и модулей. self
все еще относится к получателю:
class A
end
A.class_eval do
# self is A
self
end
Но в отличие от instance_eval
, когда вы определяете методы в блоке class_eval
, они будут доступны в экземплярах класса, а не в самом объекте класса. При class_eval
методы не добавляются в одноэлементный класс в иерархии наследования. Вместо этого методы добавляются в current class
получателя! Поэтому, когда вы определяете метод в class_eval
, этот метод переходит непосредственно в current class
и, таким образом, он становится методом экземпляра. Таким образом, вы не можете вызвать его для объекта класса; Вы можете вызывать его только на экземплярах объекта класса.