【问题标题】:What is the relationship between the metaclass of Base and Derived class in Ruby?Ruby中Base的元类和Derived类有什么关系?
【发布时间】:2011-01-09 10:04:18
【问题描述】:

在Ruby中,我们可以在单例方法中使用super来调用对应超类的单例方法,如下代码所示。

class Base
  def self.class_method
    puts "Base class method"
  end
end

class Derived < Base
  def self.class_method
    puts "Derived class method"
    super
  end
end

Derived.class_method
# Derived class method
# Base class method

但是,我似乎不太明白在Derived.class_method 中对super 的调用如何到达Base.class_method。我假设class_method 是在他们的元类上定义的,这是否意味着他们的元类具有父/子关系? (我不能通过实验完全证实)

更新:我问这个问题是因为我记得看到某处在基类和派生类的元类之间存在某种关系(但我找不到更多)。除了知道super 的实际工作原理之外,我还想确认这两个元类是否完全独立。

【问题讨论】:

    标签: ruby metaprogramming metaclass


    【解决方案1】:

    这里有四个类对象:

    <Class>---class---><Class>
    Base               #Base
       ^                  ^
       |                  |
       |                  |
     super              super
       |                  |
       |                  |
    <Class>            <Class>
    Derived---class--->#Derived
    

    命名法:

    • <...> 是每个对象的类。
    • 类的名称在第二行。
    • 如果名称以 # 开头,则它是 eigenclass(也称为单例类)。
    • super 指向一个类的超类
    • class 指向类的类。

    当你调用 Derived.class_method 时,Ruby 遵循“先右后上”的规则:先去对象的类,然后顺着超类链上去,找到方法就停止:

    • “class_method”调用的接收者是派生的。因此,沿着链向右指向 Derived 的类对象,即其特征类 (#Derived)。
    • Derived 没有定义方法,因此 Ruby 沿着链向上到达#Derived 的超类,即#Base。

    • 该方法在那里找到,因此 Ruby 将消息分派给#Base.class_method

    你不会认为我对这些事情一无所知吧?这是我的大脑得到所有这些元juju的地方:Metaprogramming Ruby

    第 2 部分。如何使“特征类”(又名“单例类”)摆脱隐藏

    class Object
      def eigenclass
        class << self
          self
        end
      end
    end
    

    此方法将返回任何对象的特征类。现在,上课呢?这些也是对象。

    p Derived.eigenclass               # => #<Class:Derived>
    p Derived.eigenclass.superclass    # => #<Class:Base>
    p Base.eigenclass                  # => #<Class:Base>
    

    注意:以上来自Ruby1.9。在 Ruby 1.8 下运行时,您会得到一个惊喜:

    p Derived.eigenclass.superclass    # => #<Class:Class>
    

    【讨论】:

    • 有没有办法“看到”#Base 和#Derived 之间的关系?例如,像普通的.superclass
    • 我添加了使特征类摆脱隐藏的技巧。但是该图与技巧显示的不一致:(
    • 好吧...# 不是#
    • @Wayne,你的理论是正确的,只是 #class 和 #super 方法永远不会返回特征类 - 特征类在 Ruby 中有些“隐藏”,请参见此处:banisterfiend.wordpress.com/2008/10/25/…
    • 制作那个#superclass(不是我所说的#super)。是的,#superclass 在 1.9 中表现不同,因为它不隐藏特征类。在 1.8 中,尽管 #superclass 返回“第一个真正超类的特征类”。因此,鉴于 Class 是第一个真正的(即非 eigenclass 或 iclass)超类 #superclass 将返回 Class 的 eigenclass :)
    【解决方案2】:

    为了澄清和纠正我在 cmets 中关于 Ruby 隐藏/暴露特征类的方式所写的内容,情况如下:

    Ruby 1.8:

    (1) Object#class 方法总是返回对象的 actual 类的第一个真正的(非 eigenclass 或 iclass)超类。 例如

    o = Object.new
    class << o; end
    o.class #=> returns Object, even though the _actual_ class is the eigenclass of o
    

    换句话说,Object#class 方法永远不会返回一个特征类,它会传递它们 而是返回它在继承层次结构中找到的第一个“真实”类。

    (2)Class#superclass方法有两种情况。如果接收者不是一个特征类,那么它只返回超类。然而,如果接收者一个特征类,那么这个方法返回接收者的实际(即不一定是真实的)类。

    # case where receiver is a normal class (i.e not an eigenclass)
    Module.superclass #=> Object (behaves as expected)
    
    # case where receiver is an eigenclass
    class << Module; superclass; end #=> returns Class, this is NOT the superclass
    

    从上面看,Class#superclass 在普通类的情况下表现如预期,但对于 eigenclass 示例,它指出 Module 的 eigenclass 的超类是 Class ,这是不正确的。从这张图http://banisterfiend.wordpress.com/2008/10/25/the-secret-life-of-singletons/我们知道Module的eigenclass的超类其实就是Object的eigenclass。我不确定为什么 Ruby 1.8 会有这种奇怪的行为。

    Ruby 1.9:

    (1) Object#class 方法的行为与 1.8 版本相同。

    (2) Class#superclass 方法不再有两种情况,它现在对待特征类的方式与对待普通类的方式相同,并按预期返回实际的超类。

    例如

    class << Module; superclass; end #=> #<Class:Object>
    

    【讨论】:

      【解决方案3】:

      此图说明了关系:http://banisterfiend.wordpress.com/2008/11/25/a-complete-ruby-class-diagram/

      此外,这里还有一些其他的帖子可以更详细地解释特征类的复杂性: http://www.klankboomklang.com/2007/10/05/the-metaclass/

      http://www.klankboomklang.com/2007/09/21/the-singleton-class/

      这是一个相当困难的解释,它解释的内容比您可能想知道的要多: http://banisterfiend.wordpress.com/2008/10/25/the-secret-life-of-singletons/

      【讨论】:

      • 我终于有时间阅读您的辛苦帖子了。好东西!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-21
      • 1970-01-01
      相关资源
      最近更新 更多