Ruby 中的单例 class 是什么?

What is a singleton class in Ruby?

我无法理解 eigenclasssingleton class in [=56] 的概念=].我读了很多 eigenclass 是 class 的 class。这对我来说没有意义,因为对我来说 class 的 class 实际上是 Class 因为所有 classes 实际上都是 class Class.

我不太明白的另一件事是以下声明:class 方法实际上是 class eigenclass 的实例方法。 eigenclass 可以通过这种方式访问​​:

YourClass = Class.new
class << YourClass
  def class_method
  end
end

但是如果eigenclass确实是YourClassclass(即Class),前面的代码不应该打开classClass 并向其添加实例方法 class_method 使其可供所有未来实例访问(即未来定义的任何常规 class)?

我其实有点觉得单例class和Class不一样。当你这样做时:

class MyClass
end

MyClass.singleton_class

你得到 #<Class:MyClass> 这与 MyClass.class => Class

的输出不同

#<Class:MyClass> 输出是什么?这与命名空间无关,否则会有两个:Class::MyClass...

我正在寻找对本征class 概念的简单而明确的解释,以阐明我的想法。

我认为您将 eigenclass 的概念与 Ruby class 实例的概念混淆了,这有点偏离了正轨。它们并不完全相同;更正确的说法是 Ruby 在内部将 class 定义实现为两个对象。

这很容易证明:

$ irb
> ObjectSpace.count_objects[:T_CLASS]
 => 1285 
> Foo = Class.new { def instance_method; end }
 => Foo 
> ObjectSpace.count_objects[:T_CLASS]
 => 1287

在内部,每个对象都有一个 klass 指针,表面上指向 class 该对象的一个​​实例。然而,每个对象(除了某些原语,如 IntegerFloatSymbol)也有一个 eigenclass 实例,这就是 klass 指针实际上指向。在 classes 的情况下,klass 指针实际上并不指向 Class class,而是指向属于 [=62] 的单例对象=] 定义,并且包含方法 table,该方法持有 class 方法。

正如 anothermh 提供的 link 所解释的那样,"ordinary" class 对象的方法 table 包含所有 class 的实例方法,而eigenclass 对象的方法 table 包含所有 class 的 class 方法。这是防止 class 的所有 class 方法被 Class class 的任何实例访问的机制。

现在,eigenclass(eigen 在 "my own" 的意义上是 "own" 的德语)是 class 的 class,这就是为什么它也被称为 metaclass 的原因。 (查看 Class::new 的源代码,您将看到对 rb_make_metaclass 的调用。)

这就是为什么调用 MyClass.singleton_class returns #Class:MyClass 而不是调用 MyClass.class 而调用 Class 的原因。此语法类似于 p Foo.new 返回类似 #<Foo:0x00007f9c0d190400> 的内容,即 class 和指向实例的指针。对于 #Class:MyClassMyClass 是指向实例的指针。因此,这描述了对 MyClass 的元 class 或 MyClass 的 class 的引用。这不要与 class MyClass 的实例混淆,后者当然是 Class.

如果您好奇的话,eigenclass 实例中的 klass 指针实际上指向它自己。 MyClass.singleton_class.singleton_class returns #Class:#Class:MyClass.

事实证明了这一点

有关更全面的概述,请参阅 Demystifying Ruby Singleton Classes. For a look at what's going on in the source code, see The Ruby Hacking Guide: Chapter 4. Finally, Ruby Under a Microscope 是全面深入了解 Ruby 内部结构的绝佳资源。

[编辑以纳入评论中的一些讨论]

Eigenclass不再是Ruby世界中使用的名称,因为Ruby官方引入了一个方法Object#singleton_class我不知道哪个版本(抱歉)。

Ruby 中的每个对象,作为 "normal" 对象或 class,甚至是单例 class,都有自己的单例 class。

单例 class 是 class.

Object.new.singleton_class.is_a?(Class)  #=> true

一个单例 class 有且只有一个实例,它是您调用 singleton_class 的对象。例如foo.singleton_class 的唯一实例是 foo.

Ruby 允许您向单个对象添加方法。

a = Object.new
b = Object.new

def a.foo
  'foo'
end

a.foo  #=> "foo"
b.foo  #=> raises NoMethodError 

但是所有的实例方法都应该定义在一个class中,那么class是在哪个a.foo中定义的呢?答案是a.singleton_class。因为a.singleton_class只有一个实例a,所以实例方法foo只能在a上调用,不能在b上调用,虽然它们属于同款。

至于class的单例classes,它们的作用是存储"normal classes'"class方法,或者稍微扭动一下脑子,绑定到单个 class 个实例的实例方法。

你不觉得Ruby的对象模型一致漂亮吗?

单例 class 包含特定于单个对象的方法。

对于通用对象,这是一个很好的功能。但是对于 classes 来说,这是至关重要的。让我们从对象开始:

对象的单例class

实例方法通常在 classes 中定义。相同 class 的所有实例共享相同的实例方法。单例 class 位于对象及其 class 之间。它允许每个实例都有自己的一组方法,独立于其他实例。

如果我们有两个 class,FooBar,每个有 2 个实例 abcd:

class Foo ; end
class Bar ; end

a = Foo.new #=> #<Foo:0x00007fc280963008>
b = Foo.new #=> #<Foo:0x00007f8319016b18>
c = Bar.new #=> #<Bar:0x00007fa66c8d7290>
d = Bar.new #=> #<Bar:0x00007f94d5106ac8>

你会有这样的 class 结构:(简化,不包括模块)

object          singleton class              class    superclass   ...

  a ── #<Class:#<Foo:0x00007fc280963008>> ─┐
                                           ├─ Foo ─┐
  b ── #<Class:#<Foo:0x00007f8319016b18>> ─┘       │
                                                   ├─ Object ── BasicObject
  c ── #<Class:#<Bar:0x00007fa66c8d7290>> ─┐       │
                                           ├─ Bar ─┘
  d ── #<Class:#<Bar:0x00007f94d5106ac8>> ─┘

Ruby 懒惰地创建那些单例 classes,例如在调用 singleton_class.

所以在定义一个方法a.hello的时候,并不是存放在a的classFoo,而是存放在a的单例class:

def a.hello
  'hello from a'
end

a.method(:hello).owner
#=> #<Class:#<Foo:0x00007fc280963008>>  <-- a's singleton class

因此,b 看不到该方法,即使两者都是 Foo 个实例:

b.hello #=> NoMethodError: undefined method `hello'

我们甚至可以为 b 定义一个同名的方法而不干扰 a:

def b.hello
  'hello from b'
end

b.method(:hello).owner
#=> #<Class:#<Foo:0x00007f8319016b18>>  <-- b's singleton class

a.hello #=> "hello from a"
b.hello #=> "hello from b"

我们还可以在 Foo 中定义一个泛型 hello 并在每个实例级别覆盖它:(您通常不这样做,但这是可能的)

class Foo
  def hello
    'hello'
  end
end

def a.hello
  "#{super} from a"
end

def b.hello
  "b says #{super.upcase}!"
end

a.hello #=> "hello from a"
b.hello #=> "b says HELLO!"

c = Foo.new
c.hello #=> "hello"

Singleton classes for classes

以上内容对于 classes 尤其重要。每个 class 都是 Class:

的一个实例
Foo.class #=> Class

假设我们想要一个方法Foo.hello,我们应该在哪里定义它?

实例方法通常在实例的class中定义,所以我们可以Foo的class中定义:

class Class
  def hello
    'Hello from Foo'
  end
end

Foo.hello
#=> "Hello from Foo"

但这将使该方法可用于 Class:

的所有实例
Bar.hello
#=> "Hello from Foo"

String.hello
#=> "Hello from Foo"

最好有一个Foo实例专属的地方。而那个地方就是Foo的单例class:

def Foo.hello
  'Hello from Foo'
end

class Foo
  def self.hello       # <-- self is Foo, so this is just "def Foo.hello"
    'hello from Foo'
  end
end

和上面的a.hello一样,此方法只适用于Foo:

Foo.hello #=> "hello from Foo"
Bar.hello #=> NoMethodError

我们将这些方法称为class方法,但它们实际上只是单例的实例方法class:

Foo.method(:hello).owner
#=> #<Class:Foo>   <-- Foo's singleton class

Foo.method(:hello).unbind == Foo.singleton_class.instance_method(:hello)
#=> true

如果您将 classes 的单例方法与对象的单例方法进行比较,您会发现它们是相同的。那是因为在 Ruby 中,classes 也是对象,并且所有对象的工作方式都相似。