从特征类确定类

Posted

技术标签:

【中文标题】从特征类确定类【英文标题】:Determine Class from Eigenclass 【发布时间】:2014-02-25 13:09:27 【问题描述】:

在 Ruby 中,获取类Foo 的特征类很简单

eigenclass = class << Foo; self; end
#=> #<Class:Foo>
eigenclass = Foo.singleton_class #2.1.0
#=> #<Class:Foo>

我对逆运算感兴趣:从 eigenclass 本身获取 eigenclass 的所有者:

klass = eigenclass.owner
#=> Foo

我不确定这是否可能,因为 eigenclass 是 Class 的匿名子类,所以 Foo 在其继承层次结构中没有出现。检查 eigenclass 的方法列表也不令人鼓舞。 eigenclass.name 返回nil。唯一让我希望这是可能的:

Class.new # normal anon class
#=> #<Class:0x007fbdc499a050>
Foo.singleton_class
#=> #<Class:Foo>

很明显,本征类的to_s 方法知道一些关于所有者的信息,即使在本征类被实例化时该信息是硬编码的。因此,我知道的唯一方法是一些 hacky Object.const_getting 之类的

Object.const_get eigenclass.to_s[/^#\<Class\:(?<owner>.+)\>$/, :owner]
#=> Foo

【问题讨论】:

更简洁:给定"foo".singleton_class的值,我们怎样才能回到"foo" 【参考方案1】:

使用ObjectSpace.each_object 将单例类传递给它以查找与给定单例类匹配的所有类:

Klass = Class.new
ObjectSpace.each_object(Klass.singleton_class).to_a  #=> [Klass]

但是,由于一个类的单例类继承自其超类的单例类,如果您要查找的类有子类,您将得到多个结果:

A = Class.new
B = Class.new(A)
B.singleton_class.ancestors.include?(A.singleton_class)  #=> true

candidates = ObjectSpace.each_object(A.singleton_class)
candidates.to_a  #=> [A, B]

幸运的是,classes/modules are sortable 在继承树中的位置(与ancestors 给出的顺序相同)。由于我们知道所有结果都必须属于同一继承树,因此我们可以使用max 来获取正确的类:

candidates.sort.last  #=> A
ObjectSpace.each_object(B.singleton_class).max  #=> B

【讨论】:

如果继承树中有分支,这似乎不起作用:class A; end; class B &lt; A; end; class C &lt; A; endObjectSpace.each_object(A.singleton_class).to_a #=&gt; [C, B, A]ObjectSpace.each_object(A.singleton_class).sort #=&gt; ArgumentError: comparison of Class with Class failed @ChrisKeele 嗯,很有趣。不幸的是,我不认为有一个优雅的方法可以解决这个问题。当然,它失败了,因为它不知道是否将B 放在C 之前,反之亦然。鉴于此,我认为您自己的答案可能是最可靠的,而且没有过度设计。 看起来很像。直到模块是可排序的,这在其他元编程上下文中非常漂亮。 我想知道比较功能是如何实现的?我的意思是它只是一个偏序,所以直观上基于比较的排序是行不通的。但是Enumerable#sort 是基于比较的,对吧?它必须是一种 hackily 的颜色子树和字典比较颜色的元组。有点奇怪。 安德鲁,有些人更喜欢max 而不是sort.last。 :-)【参考方案2】:

以与 ruby​​-implementation-agnostic 无关的方式细化 @BroiSatse 的答案,

class A; end
class B < A; end
class C < A; end
eigenclass = A.singleton_class
ObjectSpace.each_object(eigenclass).find do |klass|
  klass.singleton_class == eigenclass
end
#=> A

这在处理子类树中的分支时也是可靠的,这是@Andrew Marshall 的优雅答案不起作用的唯一原因。

【讨论】:

【参考方案3】:

使用ObjectSpace:

e = class << 'foo'; self; end

ObjectSpace.each_object(e).first    #=> 'foo'

从特征类内部获取对象:

class << 'foo'
  puts ObjectSpace.each_object(self).first
end

#=> 'foo'  

【讨论】:

这很聪明,但不是 100% 可靠:class A; end; class B &lt; A; end; ObjectSpace.each_object(A.singleton_class).first #=&gt; B 我很确定这是因为大多数实现避免实例化特征类直到需要,所以如果A 没有在其定义中调用它自己的特征类,它首先在子类化B 时被调用。 你用的是哪个 ruby​​ 版本,我还是得到上面运行代码。 我在 MRI 2.1.0-p0 ATM 上。

以上是关于从特征类确定类的主要内容,如果未能解决你的问题,请参考以下文章

是否可以让子特征从另一个特征继承类参数?

扩展特征的单元测试类 - 我如何在特征中模拟和存根方法?

【数据建模 特征分箱】特征分箱的方法

我可以直接从具有特定大小类或特征集合的 xib 加载 UITableViewCell 吗?

Java基础 | 关于面向对象三大特征与内存回收

面向对象的三大特征