В чем разница между class_eval и class << className? - PullRequest
2 голосов
/ 22 марта 2011

Я рубиновый стартер. Я обнаружил, что оба они очень похожи (в выводе), но я не мог понять разницу в следующем контексте. Например, у меня есть класс

class Say
  def self.hello
    puts "hello"
  end
end

и может быть расширено так

class << Say
  def hi
    puts "hi"
  end
end

а также вот так

Say.class_eval do
  def self.bye
    puts "bye"
  end
end

Когда мне следует использовать <<, а когда class_eval?

Ответы [ 2 ]

14 голосов
/ 22 марта 2011

class_eval на самом деле не имеет ничего общего с class << className.

A.class_eval do
   ...
end

эквивалентно

class A
  ...
end

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

class << className открывает одноэлементный класс className, позволяя вам определять методы класса.

class << A
  def foo
    ...
  end
end

Так же, как

def A.foo
  ...
end

Обратите внимание, что они являются методами класса oly, если A оказывается классом (почти) все объекты в ruby ​​имеют одноэлементные классы, и вы можете определить методы для них, используя любой из этих двух синтаксисов. Преимущество class << obj в основном в том случае, если вы определяете много одноэлементных методов за один раз.

1 голос
/ 22 марта 2011

Как уже говорилось, class_eval не имеет ничего общего с

class <<self

даже если кажется, что они делают то же самое в вашем примере (хотя эффект похож, он не делает то же самое, есть тонкие различия).

Вот еще один пример, где использование второй формы гораздо понятнее:

class A

end


a = A.new
b = A.new

class <<b
  def say_hi
    puts "Hi !"
  end
end


b.say_hi # will print "Hi !"
a.say_hi # will raise an undefined method

a и b оба являются объектами одного и того же класса A, но мы добавили метод в метакласс b, поэтому метод say_hi доступен только для объекта b.

...