Учитывая метакласс Ruby, как мне получить экземпляр, к которому он привязан? - PullRequest
6 голосов
/ 14 августа 2011

Это обратный вопрос " Если получить экземпляр объекта Ruby, как мне получить его метакласс? "

Вы можете увидеть представление объекта, для которогометакласс или одноэлементный класс присоединяется к выводу по умолчанию 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, который возвращает этот объект (или ноль, если получатель является обычным классом)?

s_meta.attached # => s
c_meta.attached # => C
C.attached # => nil

Ответы [ 4 ]

8 голосов
/ 14 августа 2011

Есть отвратительный (но работающий) взлом, использующий 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
3 голосов
/ 14 августа 2011

Я не знаю о МРТ.

В JRuby следующее возвращает то, что вы хотите:

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

A.meta.to_java.attached
1 голос
/ 15 августа 2011

Вы можете получить его из inspect (в реализации МРТ):

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

Отношения между идентификатором объекта и inspect см. в этом ответе .

1 голос
/ 14 августа 2011

Вы можете определить 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
...