Ruby 元类混淆
Posted
技术标签:
【中文标题】Ruby 元类混淆【英文标题】:Ruby metaclass confusion 【发布时间】:2012-05-18 11:56:18 【问题描述】:我了解 ruby 中的所有类都是元类 Class 的实例。而“常规”对象是这些类的实例(元类类的实例)。
但我一直在想,我的意思是类是对象的根,类本身就是 Class 的实例(称为元类,因为它的实例是类)。我在一些博客中看到了 Class 类的方法 new
的一些覆盖。
所以 Class 表现得像一个类,但它的实例是类。所以看起来我们有一个圆圈,看起来类 Class 是它自己的一个实例。
我显然在这里遗漏了一点。类Class的由来是什么?
这是一个让我困惑的例子:
class Class
def new
#something
end
end
但是关键字class
暗示了类Class 的一个实例。那么这是如何工作的呢?
【问题讨论】:
Class.class # => Class
一路下来都是乌龟!
另见The class/object paradox confusion。
【参考方案1】:
这是如何工作的
简单:它没有。无论如何,不在 Ruby 中。
就像在大多数其他语言中一样,有一些核心实体被简单地假设为存在。它们从天而降,凭空显现,神奇地出现。
在 Ruby 中,其中一些神奇的东西是:
Object
没有超类,但不能定义没有超类的类,隐式直接超类始终为Object
。 [注意:Object
可能存在实现定义的超类,但最终会有一个没有超类。]
Object
是Class
的一个实例,它是Object
的子类(这意味着间接地Object
是Object
本身的一个实例)
Class
是Module
的子类,Class
的实例
Class
是 Class
的一个实例
这些东西都不能用 Ruby 解释。
BasicObject
、Object
、Module
和 Class
都需要同时出现,因为它们具有循环依赖关系。
仅仅因为这种关系不能用 Ruby 代码表达,并不意味着 Ruby 语言规范不能说它必须如此。由实现者来想办法做到这一点。毕竟,Ruby 实现对您作为程序员所没有的对象具有一定的访问权限。
例如,Ruby 实现可以首先创建BasicObject
,将其superclass
指针和class
指针都设置为null
。
然后,它创建Object
,将其superclass
指针设置为BasicObject
,并将其class
指针设置为null
。
接下来,它创建Module
,将其superclass
指针设置为Object
,并将其class
指针设置为null
。
最后,它创建Class
,将其superclass
指针设置为Module
,并将其class
指针设置为null
。
现在,我们可以覆盖BasicObject
's、Object
's、Module
's 和Class
's class
指向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
也必须由运行时系统提供。
【讨论】:
我想你的意思是“BasicObject
的超类是nil
”,因为Object
的超类是BasicObject
。
@AndrewMarshall:这是 1.9 中的新功能。 1.8 只有Object
没有BasicObject
。
@HolgerJust 是的,但 1.8 已接近使用寿命,除非询问者指定,否则我假设是最新版本的 Ruby。
技术上,Ruby 语言规范并没有说Object
的direct 超类是nil
,只是说最终 是@ 之一987654380@ 的祖先的超类是nil
。这样,Ruby 1.9 的 BasicObject
或 MacRuby 的 NSObject
仍然符合规范。
方法超类返回类的超类,或nil。这并不意味着 nil 是超一流的......【参考方案2】:
“twist”链接给出了一个元循环。它是从根的特征类到Class
类的内置超类链接。这可以表示为
BasicObject.singleton_class.superclass == Class
了解.class
映射的线索是将此映射视为从特征类和超类链接派生的:对于对象x
,x.class
是x
的特征类的超类链中的第一个类.这可以表示为
x.class == x.eigenclass.superclass(n)
其中eigenclass
是singleton_class
的“概念别名”
(抵抗立即值问题),y.superclass(i)
表示 i
-y
的超类,n
是最小的,因此 x.eigenclass.superclass(n)
是一个类。等效地,x.eigenclass
的超类链中的特征类被跳过(参见rb_class_real,它还揭示了在 MRI 中,甚至superclass
链接也是间接实现的——它们是通过跳过“iclasses”而产生的)。
这导致每个类(以及每个特征类)的class
始终是Class
类。
图片由this diagram提供。
元类混淆有两个主要来源:
闲聊。 Smalltalk-80 对象模型包含由 Ruby 对象模型纠正的概念上的不一致。此外,Smalltalk 文献在术语中使用辩证法,不幸的是,Ruby 文献中没有充分弥补这一点。
元类的定义。目前,该定义指出元类是类的类。然而,对于所谓的“隐式元类”(Ruby 和 Smalltalk-80 的情况),更合适的定义是类的元对象。 p>
【讨论】:
【参考方案3】:是的,Class 是它自己的一个实例。它是Module的子类,也是类的实例,Module是Object的子类,Object也是Class的实例。它确实是循环的——但这是核心语言的一部分,而不是库中的东西。当我们编写 Ruby 代码时,Ruby 运行时本身没有你或我所做的相同限制。
不过,我从来没有听说过“元类”这个词用来谈论 Class。它在 Ruby 中根本没有使用太多,但当它使用时,它通常是官方称为“对象的单例类”的同义词,这比对象-模块-类三角形更令人困惑。
【讨论】:
根据一般定义,类确实可以称为元类。但是许多红宝石学家错误地将世界元类用于其他事物。但是根据一般定义,这些元类是弱元类。如果我记得很清楚,ruby 中通常称为元类的东西是持有类方法的单例。我读过几篇关于它的帖子,说元类这个词被错误地使用了,他们在这一点上指责 Rails 团队。谢谢大家。【参考方案4】:虽然它有点过时,但this article by _why 可能有助于理解这种行为。您可以在 Paolo Perrotta 的Metaprogramming Ruby 中找到更深入的主题。
【讨论】:
What Why 与 Parello 对“元类”一词的使用无关。他说的是单例类,或者更通俗地说,特征类。以上是关于Ruby 元类混淆的主要内容,如果未能解决你的问题,请参考以下文章