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)
使用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)
以与 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 优雅的答案不起作用的唯一原因。