【问题标题】:Retrieve a Ruby object from its singleton class?从它的单例类中检索一个 Ruby 对象?
【发布时间】:2026-01-06 20:10:01
【问题描述】:

可以通过以下方式从 Ruby 对象访问singleton class

some_object.singleton_class

是否可以做相反的操作:在单例类中访问原始对象?

class << some_object
  # how to reference some_object without actually typing some_object?
end

我想干这个method

class Example
  PARENTS = []
  class << PARENTS
    FATHER = :father
    MOTHER = :mother
    PARENTS.push(FATHER, MOTHER)
  end
end

并尝试用更通用的东西替换类中的PARENTS

【问题讨论】:

  • 还有singleton_class?,所以单例类很清楚它的特殊地位。
  • "没有实际输入 some_object" – 更糟糕的是:如果 some_object 是一个局部变量,它就没有在 class &lt;&lt; some_object 块中定义。尝试在块中引用它会导致 NameError

标签: ruby metaprogramming


【解决方案1】:

我不知道有任何内置方法或关键字,但您可以编写一个方法,将(单例)方法添加到对象的单例类中,并返回对象本身:

class Object
  def define_instance_accessor(method_name = :instance)
    singleton_class.define_singleton_method(method_name, &method(:itself))
  end
end

用法:

obj = Object.new              #=> #<Object:0x00007ff58e8742f0>
obj.define_instance_accessor
obj.singleton_class.instance  #=> #<Object:0x00007ff58e8742f0>

在您的代码中:

class Example
  PARENTS = []
  PARENTS.define_instance_accessor
  class << PARENTS
    FATHER = :father
    MOTHER = :mother
    instance.push(FATHER, MOTHER)
  end
end

在内部,YARV 将对象存储在名为__attached__ 的实例变量中。实例变量没有通常的 @ 前缀,因此它在 Ruby 中不可见或无法访问。

这里有一个小 C 扩展来公开它:

#include <ruby.h>

static VALUE
instance_accessor(VALUE klass)
{
    return rb_ivar_get(klass, rb_intern("__attached__"));
}

void Init_instance_accessor()
{
    rb_define_method(rb_cClass, "instance", instance_accessor, 0);
}

用法:

irb -r ./instance_accessor
> obj = Object.new
#=> #<Object:0x00007f94a11e1260>
> obj.singleton_class.instance
#=> #<Object:0x00007f94a11e1260>
>

【讨论】:

  • C 扩展的出色工作。不过,我仍然希望有一个干净的普通 Ruby 解决方案。
【解决方案2】:

只是出于好奇(请不要在家里或学校使用)

object = []
class << object
  type, id = to_s[/(?<=:#<).*?(?=>)/].split(':')
  ObjectSpace.each_object(Kernel.const_get(type)).find do |e|
    e.__id__ == id.to_i(16) >> 1
  end << :father
end   
#⇒ [:father]

【讨论】:

  • @MarcinKołodziej 是的,这是“如何在 24 小时内将所有队友变成暴力敌人”:)
【解决方案3】:

我们可以这样做。

def singleton_class_to_object(sc)
  ObjectSpace.each_object(Object).find { |o|
    (o.singleton_class == sc) rescue false }
end

o = Object.new
  #=> #<Object:0x00005b52e502d030> 
singleton_class_to_object(o.singleton_class)
  #=> #<Object:0x00005b52e502d030> 

class C; end
singleton_class_to_object(C.singleton_class)
  #=> C

内联救援是处理对象o,它们是直接对象,没有单例类。

在 MRI v2.7.0 中,

ObjectSpace.each_object(Object).to_a.size
  #=> 35362

只是微不足道的。

【讨论】:

  • 好主意。它仍然不是完全简单的,但它朝着正确的方向前进,并且不需要 C。
  • 它在原始示例中运行良好:pastebin.com/DqJh9JuB您对方法名称有更好的想法吗?
  • 对于名称,ssalc_notelgnis 怎么样?我不确定您是否要检查参数是否是方法中的单例类,但如果不是,则可能会引发异常。 (考虑一个保护子句而不是ifraise ... unless singleton_class。)另外,不应该在Class而不是Module中定义吗?
  • 另一个想法是singleton_class_of_what?
最近更新 更多