【问题标题】:Given a Ruby metaclass, how do I get the instance to which it is attached?给定一个 Ruby 元类,我如何获取它所附加的实例?
【发布时间】:2011-10-26 13:50:28
【问题描述】:

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

【问题讨论】:

    标签: ruby metaprogramming


    【解决方案1】:

    有一个丑陋的(但有效的)黑客,使用 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
    

    【讨论】:

      【解决方案2】:

      我不知道核磁共振。

      在 JRuby 中,以下返回你想要的:

      require 'java'
      class A
        def self.meta
          class << self; self; end
        end
      end
      
      A.meta.to_java.attached
      

      【讨论】:

        【解决方案3】:

        您可以定义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
        

        【讨论】:

          【解决方案4】:

          您可以从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
          

          object id与inspect的关系见this answer

          【讨论】:

            猜你喜欢
            • 2011-02-26
            • 2021-11-12
            • 1970-01-01
            • 1970-01-01
            • 2012-04-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多