Ruby元类混淆问题

14

我知道在Ruby中,所有类都是元类Class的实例。而“普通”对象则是这些类的实例(元类Class的实例)。

但我一直在想,我的意思是类是对象的根,类本身是Class类的实例(称为元类,因为它的实例是类)。我在一些博客中看到了对Class类中new方法的覆盖。

所以Class表现得像一个类,但它的实例是类。因此,我们似乎有一个循环,看起来Class类是自己的一个实例。

我显然漏掉了什么要点。Class类的起源是什么?

这里有一个让我感到困惑的示例:

class Class
  def new
    #something
  end
end

但是关键字 class 暗示着一个 Class 类的实例。那么这是怎么工作的呢?


5
Class.class # => Class - Flexoid
7
这个表达意思是“无底深渊”,用来形容一个问题或者困境没有止境,类似于一只乌龟越往下爬越深的情景。 - Andrew Grimm
1
请参阅 类/对象悖论困惑 - Andrew Marshall
4个回答

34

这怎么做

简单:它在Ruby中不起作用。

就像在大多数其他语言中一样,有一些核心实体被认为是已经存在的。它们从天而降,凭空出现,魔法般地出现。

在Ruby中,其中一些神奇事物包括:

  • Object没有超类,但你不能定义没有超类的类,隐含的直接超类始终是Object。(注意:可能有Object的实现定义超类,但最终会有一个没有超类的超类。)
  • ObjectClass的一个实例,ClassObject的子类(这意味着间接地Object也是Object的实例)。
  • ClassModule的子类,ModuleClass的一个实例。
  • ClassClass的一个实例。

这些东西都无法在Ruby中解释。

BasicObjectObjectModuleClass都需要同时存在,因为它们存在循环依赖。

仅仅因为这种关系不能在Ruby代码中表达,这并不意味着Ruby语言规范不能说明必须这样做。这取决于实现者找出一种方法来实现。毕竟,Ruby的实现具有您作为程序员所没有的对象访问级别。

例如,Ruby实现可以首先创建BasicObject,将其superclass指针和class指针都设置为null

然后,创建Object,并将其superclass指针设置为BasicObject,将其class指针设置为null

接下来,创建Module,将其superclass指针设置为Object,将其class指针设置为null

最后,创建Class,将其superclass指针设置为Module,将其class指针设置为null

现在,我们可以将BasicObjectObjectModuleClassclass指针覆盖为指向Class,然后就完成了。

这在系统外部很容易做到,但在内部看起来有点奇怪。

一旦它们存在,你完全可以在纯Ruby中实现它们的大部分行为。由于Ruby的开放类,你只需要非常基本的这些类的版本,稍后可以添加任何缺失的功能。

在您的示例中,class Class并不是创建名称为Class的新类,而是重新打开运行时环境给我们提供的现有类Class

因此,在纯Ruby中完全可以解释Class#new

class Class
  def new(*args, &block)
    obj = allocate # another magic thing that cannot be explained in Ruby
    obj.initialize(*args, &block)
    return obj
  end
end

[注意: 实际上,initialize 是私有的,因此您需要使用 obj.send(:initialize, *args, &block) 来规避访问限制。]

另外,Class#allocate 是另一个神奇的东西。它在 Ruby 的对象空间中分配了一个新的空对象,这是在 Ruby 中无法做到的事情。因此,Class#allocate 也是运行时系统必须提供的东西。


3
我认为你的意思是"BasicObject的超类是nil",因为Object的超类是BasicObject - Andrew Marshall
2
@AndrewMarshall:这是1.9版本中的新功能。1.8版本只有Object没有BasicObject - Holger Just
2
@HolgerJust 是的,但1.8版本已接近生命周期结束,除非提问者指定,否则我会假设使用最新版本的Ruby。 - Andrew Marshall
1
从技术上讲,Ruby语言规范并没有说Object直接超类是nil,只是说最终Object的祖先之一的超类是nil。这样,Ruby 1.9的BasicObject或MacRuby的NSObject仍然符合规范。 - Jörg W Mittag
1
方法superclass返回类的超类,或者返回nil。这并不意味着nil是超类... - Fivell
@Fivell:你说得对,我记错了。这是来自 Ruby 语言规范(第 15.2.1.2 条款)的相关引用:“类 Object 没有直接超类,或者可能有一个实现定义的超类。” - Jörg W Mittag

4

“扭曲”链接提供了一种元循环性。它是从根的特有类到Class类的内置超类链接。这可以用以下方式表示:

BasicObject.singleton_class.superclass == Class

理解 .class 映射的线索在于将其视为从特异类和超类链接中派生出来的映射:对于一个对象 xx.classx 的特异类的超类链中的第一个类。这可以表达为:

x.class == x.eigenclass.superclass(n)

其中,eigenclasssingleton_class的“概念别名”(对立值问题具有抵抗力),y.superclass(i)表示y的第i个超类,n是最小的使得x.eigenclass.superclass(n)是一个类的数。等价地,跳过x.eigenclass的超类链中的eigenclasses(请参见rb_class_real,它还揭示了在MRI中,即使superclass链接也是间接实现的——它们通过跳过“iclasses”而出现)。
这导致每个类(以及每个eigenclass)的class始终是Class类。 此图提供了一个例子。
元类混淆有两个主要来源:
  • Smalltalk。Smalltalk-80对象模型包含概念上的不一致,在Ruby对象模型中得到了纠正。此外,Smalltalk文献使用术语方面存在分歧,遗憾的是在Ruby文献中没有得到充分的纠正。

  • 元类的定义。目前,定义说明metaclasses是classes of classes。然而,对于所谓的“implicit metaclasses”(Ruby和Smalltalk-80的情况),更适合的定义将是类的meta-objects


3

是的,Class是它自己的一个实例。它是Module的子类,而Module也是class的一个实例,而Module又是Object的子类,Object也是Class的一个实例。 这确实有些循环 - 但这是核心语言的一部分,不是库中的内容。Ruby运行时本身在写Ruby代码时没有我们或您面临的同样的限制。

我从未听说过“元类”一词用于讨论Class。 在Ruby中它并不常用,但是当它被使用时,通常是正式称为“对象的单例类”的同义词,这比Object-Module-Class三角形更令人困惑的话题。


1
实际上,根据一般定义,Class可以被称为元类。但是许多Ruby程序员错误地将metaclass这个词用于其他事物。但是这些元类根据一般定义是弱元类。如果我没记错,在Ruby中经常被称为元类的东西是持有类方法的单例。我读过几篇关于此的文章,说metaclass这个词被错误地使用了,并且他们在这一点上指责Rails团队。谢谢大家。 - Perello

2

他所谈论的与Parello对“元类”一词的使用无关。他所说的是单例类,或者更通俗地说,特殊类。 - Chuck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接