如果类方法被覆盖,则访问类

时间:2019-06-27 02:00:22

标签: ruby

这是一个外部类,其class方法被覆盖。

class Foo
  def class
    "fooo"
  end
end

class Boo < Foo
end

class Moo < Foo
end

现在我有一个子类的实例。是否可以找出它属于哪个类?

foo.class # currently returns 'fooo', I want get Boo or Moo.

4 个答案:

答案 0 :(得分:8)

您可以使用instance_method从安全的地方(例如class)抓取Object方法作为UnboundMethod,将该未绑定方法绑定到您的实例,然后叫它。例如:

class_method = Object.instance_method(:class)
# #<UnboundMethod: Object(Kernel)#class> 

class_method.bind(Boo.new).call
# Boo 

class_method.bind(Moo.new).call
# Moo 

class_method.bind(Foo.new).call
# Foo 

当然,如果您还替换了Object#class(或Kernel#class),那么所有的赌注都消失了,您将面临一个全新的痛苦与困惑世界。

答案 1 :(得分:5)

我更喜欢@Stefan和@muistooshort的解决方案,但这是替代方案。

class Foo
  def class
    "foo"
  end
end

class Boo < Foo
end

class Who < Boo
  def class
    "who"
  end
end

boo = Boo.new
boo.method(:class).super_method.call
  #=> Boo

who = Who.new
who.method(:class).super_method.call
  #=> "foo"
who.method(:class).super_method.super_method.call
  #=> Who

更一般地:

def my_class(obj)
  m = obj.method(:class)
  until m.owner == Kernel do
    m = m.super_method
  end
  m.call
end

my_class(boo)
  #=> Boo 
my_class(who)
  #=> Who

请参见Method#super_method

答案 2 :(得分:5)

对象的类也恰好是其superclasssingleton_class

Boo.new.singleton_class.superclass
#=> Boo

Moo.new.singleton_class.superclass
#=> Moo

Foo.new.singleton_class.superclass
#=> Boo

答案 3 :(得分:3)

此解决方案有点笨拙,但覆盖class本身也是很笨拙的,因此在罗马时,请像罗马人那样做:

class Foo
  def class
    'fooo'
  end
end

class Boo < Foo
end

boo = Boo.new
=> #<Boo:0x00007fd2361feba8>

boo.class
=> "fooo"

boo.inspect
=> "#<Boo:0x00007fd2361feba8>"

klass = boo.inspect.split(':').reject(&:empty?)[0..-2].join('::').sub('#<', '')
=> "Boo"

boo.is_a?(Kernel.const_get(klass))
=> true

这也适用于模块中的类:

module Bar
  class Foo
    def class
      'fooo'
    end
  end

  class Boo < Foo
  end
end

boo = Bar::Boo.new
=> #<Bar::Boo:0x00007fe5a20358b0>

boo.class
=> "fooo"

boo.inspect
=> "#<Bar::Boo:0x00007fe5a20358b0>"

klass = boo.inspect.split(':').reject(&:empty?)[0..-2].join('::').sub('#<', '')
=> "Bar::Boo"

boo.is_a?(Kernel.const_get(klass))
=> true