从特征类确定类

Chr*_*ele 9 ruby metaprogramming class eigenclass

在Ruby中,获取类的本征类Foo是一个简单的

eigenclass = class << Foo; self; end
#=> #<Class:Foo>
eigenclass = Foo.singleton_class #2.1.0
#=> #<Class:Foo>
Run Code Online (Sandbox Code Playgroud)

我对逆操作感兴趣:从本征类本身获取本征类的所有者:

klass = eigenclass.owner
#=> Foo
Run Code Online (Sandbox Code Playgroud)

我不确定这是否可行,因为本征类是其匿名子类Class,因此Foo在其继承层次结构中无处可见.检查本征类的方法列表也不令人鼓舞.eigenclass.name回报nil.唯一让我希望这是可能的:

Class.new # normal anon class
#=> #<Class:0x007fbdc499a050>
Foo.singleton_class
#=> #<Class:Foo>
Run Code Online (Sandbox Code Playgroud)

显然,本征类的to_s方法知道关于所有者的一些事情,即使在实例化特征类时该信息是硬编码的.因此,我所知道的唯一方法Object.const_getting就是那样的hacky

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

And*_*all 6

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

Klass = Class.new
ObjectSpace.each_object(Klass.singleton_class).to_a  #=> [Klass]
Run Code Online (Sandbox Code Playgroud)

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

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]
Run Code Online (Sandbox Code Playgroud)

幸运的是,类/模块可以按它们在继承树中的位置进行排序(相同的顺序ancestors给出).由于我们知道所有结果必须是同一继承树的一部分,因此我们可以max获取正确的类:

candidates.sort.last  #=> A
ObjectSpace.each_object(B.singleton_class).max  #=> B
Run Code Online (Sandbox Code Playgroud)


Chr*_*ele 3

以与 ruby​​ 实现无关的方式完善@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
Run Code Online (Sandbox Code Playgroud)

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