从特征类确定类
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 < A; end; class C < A; end
ObjectSpace.each_object(A.singleton_class).to_a #=> [C, B, A]
ObjectSpace.each_object(A.singleton_class).sort #=> 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 < A; end; ObjectSpace.each_object(A.singleton_class).first #=> B
我很确定这是因为大多数实现避免实例化特征类直到需要,所以如果A
没有在其定义中调用它自己的特征类,它首先在子类化B
时被调用。
你用的是哪个 ruby 版本,我还是得到上面运行代码。
我在 MRI 2.1.0-p0 ATM 上。以上是关于从特征类确定类的主要内容,如果未能解决你的问题,请参考以下文章