为什么在 ruby​​ 中创建元类?

2024-05-06

我正在尝试了解 Ruby 对象模型。我知道实例方法保存在类中而不是类的对象中,因为它消除了冗余。我读到,每当创建一个类时,也会为新创建的类创建一个元类。元类存储类方法。即该类的单例方法位于元类中。例如

class MyClass
  def hi
    'hi object'
  end

  def self.bye
    'bye singleton method'
  end
end

对于上面的 MyClass,还创建了一个元类(例如#MyClass)。现在方法“hi”是一个实例级方法,可以在 MyClass 的所有对象上调用。方法“bye”是 MyClass 的单例方法,它驻留在 #MyClass 中。之所以将“hi”保存在 MyClass 而不是 MyClass 的所有对象中(我认为是这样),是因为它避免了冗余。但是我们不能有多个名为 MyClass 的类。那么为什么不在 MyClass 中而不是在 #MyClass 中存储“再见”,因为我们不能有多个 MyClass。我完全不知道为什么会这样,我只是想了解其背后的原因。

- - -更新 - -

元类存储类信息,例如单例方法和其他内容。但是,由于类是单例对象(它是类 Class 的实例,并且是其类型的唯一对象),那么为什么不将所有信息保存在类本身而不是元​​类中。


只是为了超级清楚。

这是一个解释这个问题的快速 ruby​​ 脚本:

#!/usr/bin/env ruby
puts ObjectSpace.count_objects[:T_CLASS] #>> 471
class X
  def self.foo
  end
  def bar
  end
end
puts ObjectSpace.count_objects[:T_CLASS] #>> 473

这就是 OP 所说的“ObjectSpace.count_objects[:T_CLASS] 将计数增加 2”的含义。我们将这个额外的类称为 X 的单例类,因为这似乎是 Ruby 内部对它的称呼。

irb> X
=> X
irb> X.singleton_class
=> <Class: X>

请注意,#foo方法是一个实例方法X.singleton_class, not X.

irb> X.instance_methods(false)
=> [:baz]
irb> X.singleton_class.instance_methods(false)
=> [:foo]

那么为什么是:foo存储在X.singleton_class代替X?是不是永远只有一个X?

我认为主要原因是一致性。考虑以下有关普通实例对象的更简单的场景。

car = Car.new
def car.go_forth_and_conquer
end

正如 @mikej 精彩地解释的那样,这个新方法存储在 car 的单例类中。

irb> car.singleton_class.instance_methods(false)
=> [:go_forth_and_conquer]

类是对象

现在,类也是对象。每个类都是一个实例Class。因此,当一堂课(比如X) 被定义,ruby 实际上创建了一个实例Class,然后向实例添加方法(类似于我们所做的car上面。)例如,这是创建新类的另一种方法

Car = Class.new do
  def go_forth_and_conquer
    puts "vroom"
  end
end
Car.new.go_forth_and_conquer

因此,重用代码并以相同的方式执行会更容易(即保留foo in X.singleton_class。)这可能需要更少的努力,并且会导致更少的意外,因此没有人需要编写代码来处理Class实例与其他实例不同。

可能并不重要

您可能会想,如果 Ruby 没有用于实例的单例类Class,可能会节省一些内存。然而,在我看来,哪里bar实际上存储的是我们可能不应该依赖的实现细节。 Rubinius、MRI 和 JRuby 都可以以不同的方式存储方法和实例,只要行为一致即可。据我们所知,Ruby 可能有一个合理的实现,它不会急切地为类对象创建单例类,原因与您概述的完全相同,只要整体行为符合 ruby​​ 规范。 (例如,一个实际的单例类直到#singleton_class首先调用方法。)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么在 ruby​​ 中创建元类? 的相关文章

随机推荐