Разница между "классом A; классом B" и "классом A :: B" - PullRequest
6 голосов
/ 16 марта 2012

Какая разница между:

class A
  class B
  end
end

и

class A
end

class A::B
end

Обновление: эти 2 подхода не совсем одинаковы.

Во втором подходе B не имеет доступа к константам, определенным в A.

Также, как правильно сказал Матеус Морейра, во втором подходе A должно быть определено до того, как A::B может быть определено.

Какие еще различия есть?

Ответы [ 3 ]

8 голосов
/ 16 марта 2012

В Ruby модули и классы являются экземплярами классов Module и Class соответственно. Они получают свои имена из константы , которой они назначены. Когда вы пишете:

class A::B
  # ...
end

Вы эффективно пишете:

A::B ||= Class.new do
  # ...
end

Это допустимый синтаксис присвоения констант, и предполагает , что константа A правильно инициализирована и , что она ссылается на Module или Class.

Например, рассмотрим, как обычно определяются классы:

class A
  # ...
end

То, что эффективно происходит, таково:

Object::A ||= Class.new do
  # ...
end

Теперь, когда вы пишете:

class A
  class B
    # ...
  end
end

То, что происходит на самом деле, выглядит следующим образом:

(Object::A ||= Class.new).class_eval do
  (A::B ||= Class.new).class_eval do
    # ...
  end
end

Вот что происходит, по порядку:

  1. Новый экземпляр Class назначается константе A, равной Object, если только он не был инициализирован.
  2. Новый экземпляр Class присваивается константе B, равной A, если только он не был инициализирован.

Это обеспечивает существование всех внешних классов перед попыткой определить любых внутренних классов.

Существует также изменение в области действия, которое позволяет вам напрямую обращаться к константам A. Сравните:

class A
  MESSAGE = "I'm here!"
end

# Scope of Object
class A::B
  # Scope of B
  puts MESSAGE  # NameError: uninitialized constant A::B::MESSAGE
end

# Scope of Object
class A
  # Scope of A
  class B
    # Scope of B
    puts MESSAGE  # I'm here!
  end
end

Согласно этой записи в блоге , основная команда Ruby называет "текущий класс" cref. К сожалению, автор не уточняет, но, как он отмечает, это отдельно от контекста self.


Как объяснено здесь , cref представляет собой связанный список , который представляет собой вложение модулей в некоторый момент времени.

Текущий cref используется для поиска констант и переменных класса и для def, undef и alias.


Как уже говорили другие, это разные способы выразить одно и то же.

Однако есть небольшая разница. Когда вы пишете class A::B, вы предполагаете , что класс A уже определен. Если этого не произошло, вы получите NameError, а B не будет определен вообще.

Написание правильно вложенных модулей:

class A
  class B
  end
end

Гарантирует, что класс A существует, прежде чем пытаться определить B.

3 голосов
/ 16 марта 2012

Два разных способа сказать одно и то же. Дело в том, что класс B является внутренним или вложенным классом и доступен только через интерфейс A.

> class A
..  def say
....  "In A"
....end
..
..  class B
....  def say
......  "In B"
......end
....end
..end
=> nil

> A.new.say
=> "In A"
> B.new.say
=> #<NameError: uninitialized constant B>
> A::B.new.s­ay
=> "In B"

против

> class A
..  def say
....  "In A"
....end
..end
=> nil

> class A::B
..  def say
....  "In B"
....end
..end
=> nil

> A.new.say
=> "In A"
> B.new.say
=> #<NameError: uninitialized constant B>
> A::B.new.s­ay
=> "In B"
>  
1 голос
/ 16 марта 2012

Они одинаковы.Это разные способы написания одного и того же.Первый - это наивный способ написания, но часто становится трудно отследить вложение, когда класс / модуль становится большим.Используя второй способ, вы можете избежать вложенности во внешний вид.

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