Ruby 中的单例类是什么?

Dav*_*mar 3 ruby class eigenclass

我无法理解红宝石中特征类单例类的概念。我读了很多,认为 eigenclass是一个类的类。这对我来说没有意义,因为对我来说,类的类实际上是Class因为所有类实际上都是类的实例Class

另一件我不太明白的事情是下面的说法:类方法实际上是类 eigenclass 的实例方法。特征类可以通过以下方式访问:

YourClass = Class.new
class << YourClass
  def class_method
  end
end
Run Code Online (Sandbox Code Playgroud)

但是,如果特征类确实是 YourClass 类(即Class),那么前面的代码不应该打开该类Class并向其添加实例方法class_method,使其可供所有未来的实例访问(即在未来)?

我实际上感觉单例类与Class. 当你这样做时:

class MyClass
end

MyClass.singleton_class
Run Code Online (Sandbox Code Playgroud)

你得到的#<Class:MyClass>结果与输出不同MyClass.class => Class

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

我正在寻找对本征类概念的简单且明确的解释,以澄清我的想法。

Ste*_*fan 11

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

\n\n

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

\n\n

对象的单例类

\n\n

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

\n\n

如果我们有两个类,Foo并且Bar每个类都有 2 个实例a,bc, d

\n\n
class Foo ; end\nclass Bar ; end\n\na = Foo.new #=> #<Foo:0x00007fc280963008>\nb = Foo.new #=> #<Foo:0x00007f8319016b18>\nc = Bar.new #=> #<Bar:0x00007fa66c8d7290>\nd = Bar.new #=> #<Bar:0x00007f94d5106ac8>\n
Run Code Online (Sandbox Code Playgroud)\n\n

您将拥有这样的类结构:(简化,不包括模块)

\n\n
class Foo ; end\nclass Bar ; end\n\na = Foo.new #=> #<Foo:0x00007fc280963008>\nb = Foo.new #=> #<Foo:0x00007f8319016b18>\nc = Bar.new #=> #<Bar:0x00007fa66c8d7290>\nd = Bar.new #=> #<Bar:0x00007f94d5106ac8>\n
Run Code Online (Sandbox Code Playgroud)\n\n

Ruby 会惰性地创建这些单例类,例如在调用singleton_class.

\n\n

所以定义方法时a.hello,它不是存储在a类中Foo,而是存储在a单例类中:

\n\n
def a.hello\n  \'hello from a\'\nend\n\na.method(:hello).owner\n#=> #<Class:#<Foo:0x00007fc280963008>>  <-- a\'s singleton class\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
b.hello #=> NoMethodError: undefined method `hello\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
def b.hello\n  \'hello from b\'\nend\n\nb.method(:hello).owner\n#=> #<Class:#<Foo:0x00007f8319016b18>>  <-- b\'s singleton class\n\na.hello #=> "hello from a"\nb.hello #=> "hello from b"\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
class Foo\n  def hello\n    \'hello\'\n  end\nend\n\ndef a.hello\n  "#{super} from a"\nend\n\ndef b.hello\n  "b says #{super.upcase}!"\nend\n\na.hello #=> "hello from a"\nb.hello #=> "b says HELLO!"\n\nc = Foo.new\nc.hello #=> "hello"\n
Run Code Online (Sandbox Code Playgroud)\n\n

类的单例类

\n\n

上述内容对于课堂尤其重要。每个类都是一个实例Class

\n\n
Foo.class #=> Class\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

实例方法通常定义在实例的类中,因此我们可以Foo在类中定义它:

\n\n
class Class\n  def hello\n    \'Hello from Foo\'\n  end\nend\n\nFoo.hello\n#=> "Hello from Foo"\n
Run Code Online (Sandbox Code Playgroud)\n\n

但这将使该方法可用于 的所有实例Class

\n\n
Bar.hello\n#=> "Hello from Foo"\n\nString.hello\n#=> "Hello from Foo"\n
Run Code Online (Sandbox Code Playgroud)\n\n

最好有一个实例专用的地方Foo。那个地方是Foo\ 的单例类:

\n\n
def Foo.hello\n  \'Hello from Foo\'\nend\n
Run Code Online (Sandbox Code Playgroud)\n\n

或者

\n\n
class Foo\n  def self.hello       # <-- self is Foo, so this is just "def Foo.hello"\n    \'hello from Foo\'\n  end\nend\n
Run Code Online (Sandbox Code Playgroud)\n\n

就像a.hello上面一样,此方法仅适用于Foo

\n\n
Foo.hello #=> "hello from Foo"\nBar.hello #=> NoMethodError\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
Foo.method(:hello).owner\n#=> #<Class:Foo>   <-- Foo\'s singleton class\n\nFoo.method(:hello).unbind == Foo.singleton_class.instance_method(:hello)\n#=> true\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n