Почему instance_eval () определяет метод класса при вызове класса? - PullRequest
5 голосов
/ 23 мая 2009
Foo = Class.new
Foo.instance_eval do
  def instance_bar
    "instance_bar"
  end
end
puts Foo.instance_bar       #=> "instance_bar"
puts Foo.new.instance_bar   #=> undefined method ‘instance_bar’

Насколько я понимаю, вызов instance_eval для объекта должен позволить вам определить переменную или метод экземпляра для этого объекта.

Но в приведенном выше примере, когда вы вызываете его в классе Foo для определения метода instance_bar, instance_bar становится методом класса , который можно вызывать с помощью "Foo.instance_bar". Понятно, что этот код не создал метод экземпляра, потому что Foo.new.instance_bar приводит к «неопределенному методу« instance_bar »».

Почему instance_eval определяет метод класса, а не метод экземпляра в этом контексте?

Ответы [ 2 ]

9 голосов
/ 23 мая 2009

x.instance_eval меняет ваш контекст, поэтому self оценивается как x.

Это позволяет вам делать много вещей, включая определение переменных экземпляра и методов экземпляра, но только для x.

 x = Object.new
 y = Object.new

 # define instance variables for x and y
 x.instance_eval { @var = 1 }
 y.instance_eval { @var = 2 }

 # define an instance method for all Objects
 class Object
   def var
     @var
   end
 end

 x.var #=> 1
 y.var #=> 2

Ruby позволяет вам определять методы экземпляра для объекта в нескольких местах. Обычно, каждый определяет их в классе, и эти методы экземпляра являются общими для всех экземпляров этого класса (как def var выше).

Однако мы также можем определить метод экземпляра только для одного объекта:

# here's one way to do it
def x.foo
  "foo!"
end
# here's another
x.instance_eval do
  # remember, in here self is x, so bar is attached to x.
  def bar
    "bar!"
  end
end

Даже если x и y имеют один и тот же класс, они не разделяют эти методы, поскольку они были определены только для x.

x.foo #=> "foo!"
x.bar #=> "bar!"
y.foo #=> raises NoMethodError
y.bar #=> raises NoMethodError

Теперь в ruby ​​все объекты, даже классы. Методы класса являются просто методами экземпляра для этого объекта класса.

# we have two ways of creating a class:
class A 
end
# the former is just syntatic sugar for the latter
B = Class.new

# we have do ways of defining class methods:

# the first two are the same as for any other object
def A.baz
  "baz!"
end
A.instance_eval do
   def frog
     "frog!"
   end
end

# the others are in the class context, which is slightly different
class A
  def self.marco
    "polo!"
  end
  # since A == self in here, this is the same as the last one.
  def A.red_light
    "green light!"
  end

  # unlike instance_eval, class context is special in that methods that
  # aren't attached to a specific object are taken as instance methods for instances
  # of the class
  def example
     "I'm an instance of A, not A itself"
  end
end
# class_eval opens up the class context in the same way
A.class_eval do
  def self.telegram
    "not a land shark"
  end
end

Обратите внимание, что все эти методы * A -специфичны, B не получает доступ ни к одному из них:

A.baz #=> "baz!"
B.telegram #=> raises NoMethodError

Здесь важно отнять Методы класса являются просто методами экземпляра объекта класса Class

1 голос
/ 29 апреля 2014

Целью 'instance_eval' является расширение объектов, но целью 'class_eval' является расширение классов. И поскольку классы также являются объектами, вы можете применять instance_eval к ​​классам.

Полагаю, что расширение классов просто более понятно в классическом ООП. Динамические языки позволяют нам легко определять поведение определенных объектов. Тот факт, что каждый объект может иметь собственное поведение, добавляет гибкости при разработке приложения. Не только данные могут варьироваться для объектов одного и того же класса. Два человека отличаются не только потому, что они родились в разные годы, не только потому, что у них разные родители, но они могут думать по-разному и, следовательно, вести себя по-разному.

Способность изменить поведение каждого объекта является фундаментальной. Он существует на многих языках.

Думая об instance_eval, сначала подумайте об объектах. Тогда вы поймете, что классы также являются объектами - объектами, которые дополнительно предназначены для создания новых объектов, для хранения описания общего поведения (методов). Вы можете не только использовать определение класса, но также можете назначать класс переменной, передавать класс в качестве аргумента, вызывать метод класса, программировать класс!

Я бы порекомендовал статьи, написанные Иегудой Кацем и Югуи, чтобы углубиться в это:

...