这是问题“Given an instance of a Ruby object, how do I get its metaclass?”
的反转您可以在默认的to_s
输出中看到附加了元类或单例类的对象的表示形式:
s = "hello"
s_meta = class << s; self; end
s_meta.to_s # => "#<Class:#<String:0x15004dd>>"
class C; end
c_meta = class << C; self; end
c_meta.to_s # => "#<Class:C>"
是否可以实现返回此对象的方法Class.attached
(如果接收方是常规类,则为nil)?
s_meta.attached # => s
c_meta.attached # => C
C.attached # => nil
答案 0 :(得分:8)
使用ObjectSpace有一个丑陋(但仍在工作)的黑客攻击。比如,除了玩游戏和调试之外你不应该使用的东西。你只想要它的第一个(也是唯一的)实例,所以:
ObjectSpace.each_object(self).first
要确定它是否是单例类,您可以使用ancestors
如果它是单例类(或本征类或魔法类)将不包含其接收器的奇怪属性:
ObjectSpace.each_object(self).first unless ancestors.include? self
如果你关心边缘,那么有三个对象,它们的类也是它们的单例类。
[true, false, nil].each do |o|
o.class.send(:define_method, :attached) { o }
end
答案 1 :(得分:3)
我不知道MRI。
在JRuby中,以下内容返回您想要的内容:
require 'java'
class A
def self.meta
class << self; self; end
end
end
A.meta.to_java.attached
答案 2 :(得分:1)
您可以定义metaclass
来存储附加的对象。
class Class
attr_accessor :attached
end
class Object
def metaclass
meta = class << self; self; end
meta.attached = self
meta
end
end
class A; end
a = A.new
a_meta = a.metaclass
p a #=> #<A:0xb74ed768>
p a_meta #=> #<Class:#<A:0xb74ed768>>
obj = a_meta.attached
p obj #=> #<A:0xb74ed768>
puts obj == a #=> true
p A.attached #=> nil
答案 3 :(得分:1)
您可以从inspect
(在MRI实施中)获取它:
class Class
def attached
# first, match the object reference from inspect
o_ref = inspect.match /0x([0-9a-f]+)>>$/
# if not found, it's not a metaclass
return nil unless o_ref
# calculate the object id from the object reference
o_id = (o_ref[1].to_i(16) >> 1) - 0x80000000
# get the object from its id
ObjectSpace._id2ref o_id
end
end
# testing...
class A; end
a = A.new
a_meta = class << a; self; end
p a #=> #<A:0xb7507b00>
p a_meta #=> #<Class:#<A:0xb7507b00>>
p a_meta.attached #=> #<A:0xb7507b00>
p a == a_meta.attached #=> true
p A.attached #=> nil
有关对象ID与inspect
之间的关系,请参阅this answer。