Ruby 中的单例 class 是什么?
What is a singleton class in Ruby?
我无法理解 eigenclass 或 singleton 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 该对象的一个实例。然而,每个对象(除了某些原语,如 Integer
、Float
和 Symbol
)也有一个 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:MyClass
,MyClass
是指向实例的指针。因此,这描述了对 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,Foo
和 Bar
,每个有 2 个实例 a
、b
和 c
,d
:
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 也是对象,并且所有对象的工作方式都相似。
我无法理解 eigenclass 或 singleton 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 该对象的一个实例。然而,每个对象(除了某些原语,如 Integer
、Float
和 Symbol
)也有一个 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:MyClass
,MyClass
是指向实例的指针。因此,这描述了对 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,Foo
和 Bar
,每个有 2 个实例 a
、b
和 c
,d
:
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 也是对象,并且所有对象的工作方式都相似。