Во-первых, хотя кажется, что eigentclass используется некоторыми людьми, singleton class - более распространенный термин. Класс Singleton содержит объектно-ориентированное поведение для объекта в Ruby. Вы не можете создавать другие экземпляры этого класса, кроме исходного объекта, которому принадлежит этот класс-одиночка.
Говоря об определении методов внутри различных типов eval в этой статье вводится хорошее правило для методов, определенных в instance_eval
и class_eval
:
Use ClassName.instance_eval to define class methods.
Use ClassName.class_eval to define instance methods.
Это в значительной степени описывает ситуацию.
Была огромная рецензия на классы, которые являются экземплярами класса Class, их одноэлементные классы, которые являются подклассами класса Class, и некоторые другие сумасшедшие вещи (не слишком связанные с проблемой). Но так как ваш вопрос может быть легко применен к обычным объектам и их классам (и это значительно упрощает объяснение), я решил удалить все это (хотя вы все еще можете видеть это в истории изменений ответа).
Давайте посмотрим на обычный класс и экземпляр этого класса и посмотрим, как все это работает:
class A; end
a = A.new
Определения методов внутри различных типов eval:
# define instance method inside class context
A.class_eval { def bar; 'bar'; end }
puts a.bar # => bar
puts A.new.bar # => bar
# class_eval is equivalent to re-opening the class
class A
def bar2; 'bar2'; end
end
puts a.bar2 # => bar2
puts A.new.bar2 # => bar2
Определение специфичных для объекта методов:
# define object-specific method in the context of object itself
a.instance_eval { def foo; 'foo'; end }
puts a.foo # => foo
# method definition inside instance_eval is equivalent to this
def a.foo2; 'foo2'; end
puts a.foo2 # => foo2
# no foo method here
# puts A.new.foo # => undefined method `foo' for #<A:0x8b35b20>
Давайте теперь посмотрим на одноэлементный класс объекта a
:
# singleton class of a is subclass of A
p (class << a; self; end).ancestors
# => [A, Object, Kernel, BasicObject]
# define instance method inside a's singleton class context
class << a
def foobar; 'foobar'; end;
end
puts a.foobar # => foobar
# as expected foobar is not available for other instances of class A
# because it's instance method of a's singleton class and a is the only
# instance of that class
# puts A.new.foobar # => undefined method `foobar' for #<A:0x8b35b20>
# same for equivalent class_eval version
(class << a; self; end).class_eval do
def foobar2; 'foobar2'; end;
end
puts a.foobar2 # => foobar2
# no foobar2 here as well
# puts A.new.foobar2 # => undefined method `foobar2' for #<A:0x8b35b20>
Теперь давайте посмотрим на переменные экземпляра:
# define instance variable for object a
a.instance_eval { @x = 1 }
# we can access that @x using same instance_eval
puts a.instance_eval { @x } # => 1
# or via convenient instance_variable_get method
puts a.instance_variable_get(:@x) # => 1
А теперь для экземпляра переменных внутри class_eval
:
# class_eval is instance method of Module class
# so it's not available for object a
# a.class_eval { } # => undefined method `class_eval' for #<A:0x8fbaa74>
# instance variable definition works the same inside
# class_eval and instance_eval
A.instance_eval { @y = 1 }
A.class_eval { @z = 1 }
# both variables belong to A class itself
p A.instance_variables # => [:@y, :@z]
# instance variables can be accessed in both ways as well
puts A.instance_eval { @y } # => 1
puts A.class_eval { @z } # => 1
# no instance_variables here
p A.new.instance_variables # => []
Теперь, если вы замените класс A
на класс Class
и объект a
на объект Klass
(что в данной конкретной ситуации является не чем иным, как экземпляром класса Class
), я надеюсь, вы получите объяснение на ваши вопросы. Если у вас все еще есть вопросы, не стесняйтесь спрашивать.