给定Ruby元类,如何获取它所附加的实例?

时间:2011-08-13 21:38:36

标签: ruby metaprogramming

这是问题“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

4 个答案:

答案 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