Разница между instance_eval и class << self? - PullRequest
4 голосов
/ 27 апреля 2011

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

class Example
  puts self

  class << self
    puts self
  end

  instance_eval do
    puts self
  end
end

Однако вывод:

Example
#<Class:Example>
Example

Вот мое обоснование:

  • Example является экземпляром Class, поэтому self в теле класса относится к этому;
  • class << obj устанавливает self на то, что obj находится в данном блоке,который в моем случае является экземпляром Class, то есть Example (в этом я, вероятно, ошибаюсь);
  • instance_eval запускает блок в данном экземпляре, поэтому в моем случае этопочти так же, как помещение кода в блок непосредственно в теле класса.

В настоящее время я предполагаю, что class << self вставляет класс-призрак между Example и Class и устанавливает для себя значениеэто, но вывод #<Class:Example> совсем не подтверждает это.

Так что же не так с моим обоснованием?

Ответы [ 2 ]

6 голосов
/ 27 апреля 2011

class << obj устанавливает self на то, что obj находится в данном блоке, что в моем случае является экземпляром Class, то есть Example (здесь я, вероятно, ошибаюсь);

Нет, class << obj открывает одноэлементный класс из obj. Как вы правильно указали, внутри объявления класса self относится к самому классу, поэтому в данном случае «внутренний» self (то есть тот, который передается в puts) относится к синглтон класс из Example.

3 голосов
/ 27 апреля 2011

По моему мнению, class << self был одним из самых неприятных битов синтаксиса в Ruby.Люди, плохо знакомые с языком, понятия не имеют, что он означает, кроме конвенций о культе груза, и даже те, кто близко знаком с языком, имеют лишь смутное понимание того, что отличает его от instance_method, поскольку оба они кажутся удивительно похожими.

Вот пример двух разных способов определения метода класса:

class Example
  class << self
    def class_def
      :class_def
    end
  end

  instance_eval do
    def instance_def
      :instance_def
    end
  end
end

Вы можете проверить, работают ли они, вызвав методы:

puts Example.class_def.inspect
# => :class_def
puts Example.instance_def.inspect
# => :instance_def

Разница заключается в том, что вы динамически создаете методы с использованием define_method, поскольку в версии instance_eval привязка оказывается неверной:

class Example
  class << self
    define_method(:class_def) do
      :class_def
    end
  end

  instance_eval do
    define_method(:instance_def) do
      :instance_def
    end
  end
end

Это приводит к определению метода instance_def, но небудучи привязанным к самому классу:

puts Example.class_def.inspect
# => :class_def
puts Example.instance_def.inspect
# => NoMethodError: undefined method ‘instance_def’ for Example:Class

Единственный надежный способ создания динамических методов - это class << self.Метод instance_def создается и отбрасывается, поскольку он не отображается в методах Example. даже внутри этого блока.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...