С https://bugs.ruby-lang.org/issues/16788:
При создании класса автоматически создается одноэлементный класс (который недоступен для пользователя). Ссылка на одноэлементный класс класса автоматически создает одноэлементный класс этого одноэлементного класса. Это необходимо для обеспечения согласованности структуры наследования метаклассов. В противном случае методы класса не наследуются от метакласса суперкласса, что необходимо, поскольку методы класса суперкласса должны быть доступны в качестве методов класса подкласса.
Немного изменив код вопроса:
$old_classes = []
def print_objects
new_classes = []
ObjectSpace.each_object(Class){|x| new_classes << x}
puts "New classes: #{new_classes - $old_classes}" unless $old_classes.empty?
puts "Counts: #{ ObjectSpace.count_objects[:T_CLASS] }"
$old_classes = new_classes
end
print_objects
class Test
end
puts 'Test class created'
print_objects
class Test
def self.foo
end
end
puts 'Test singleton class referenced'
print_objects
Я получаю следующие результаты:
Counts: 690
Test class created
New classes: [Test]
Counts: 692
Test singleton class referenced
New classes: [#<Class:Test>]
Counts: 693
Я пробовал с Ruby 2.6 и 2.0 как внутри, так и снаружи консоли (цифры различаются, но разница одинакова) и @SajibHassan с 1.9.3 (версия, в которой был введен метод count_objects
). Это означает, что разница всегда составляла 3 и что первый созданный синглтон-класс недоступен для пользователя.
Книга Ruby Под микроскопом (написана в 2012 году после выпуска из Ruby 2.1) также описывает создание только двух метаклассов, которые не соответствуют полученному нами результату.
Обратите внимание, что такие методы, как Module#prepend
(введены в Ruby 2.0), о которых упоминалось @ JörgWMittag в комментариях в качестве возможной причины для этого дополнительного класса, используйте T_ICLASS
. Проверьте фиксацию, в которой был введен метод для подробностей. Я предполагаю, что T_ICLASS
обозначает внутренний класс, и, следовательно, внутренние классы не должны быть видны пользователю (что имеет смысл). Я не уверен, однако, почему некоторые T_CLASS
доступны для пользователя, а другие нет.