简体   繁体   English

Ruby类层次结构中`prepend`的行为

[英]Behaviour of `prepend` in Ruby class hierarchies

I have a class Base , and two classes Derived and Derived2 that inherit from Base . 我有一个类Base ,以及两个继承自Base DerivedDerived2类。 They each have a function foo defined in them. 它们每个都有一个定义的函数foo

I also have a module Gen which is prepend -ed to Base . 我也有一个模块Genprepend -ed到Base It is also prepend -ed to Derived2 but not to Derived . 它也是Derived2 prepend ,但不是Derived

When I call foo on an instance of Derived2 , the result is as if the Gen module was only prepend -ed to Base and not to Derived2 also. 当我在Derived2一个实例上调用foo时,结果就好像Gen模块只是prependBase而不是Derived2 Is this the expected behavior? 这是预期的行为吗?

Here is the code for the above scenario: 以下是上述场景的代码:

module Gen
  def foo
    val = super
    '[' + val + ']'
  end
end

class Base
  prepend Gen

  def foo
    "from Base"
  end
end

class Derived < Base
  def foo
    val = super
    val + "from Derived"
  end
end

class Derived2 < Base
  prepend Gen
  def foo
    val = super
    val + "from Derived"
  end
end

Base.new.foo     # => "[from Base]"

Derived.new.foo  # => "[from Base]from Derived"

Derived2.new.foo # => "[from Base]from Derived"

I expected the last of the above statement to output: 我期望输出上述语句的最后一个:

[[from Base]from Derived]

To help you understand, there is a method Class#ancestors , which tells you the order in which a method will be searched for. 为了帮助您理解,有一个方法Class#ancestors ,它告诉您搜索方法的顺序。 In this case: 在这种情况下:

Base.ancestors     # => [Gen, Base, Object, Kernel, BasicObject]
Derived.ancestors  # => [Derived, Gen, Base, Object, Kernel, BasicObject]
Derived2.ancestors # => [Gen, Derived2, Gen, Base, Object, Kernel, BasicObject]

So when you call a method on an object that is an instance of said class, that method will be searched in the corresponding list in that order. 因此,当您在作为所述类的实例的对象上调用方法时,将按该顺序在相应的列表中搜索该方法。

  • Prepending puts a module in front of that list. 预先将模块放在该列表的前面。
  • Inheriting puts the parent's chain at the end of the child's (not exactly, but for simplicity's sake). 继承将父级链放在子级的末尾(不完全是,但为了简单起见)。
  • super just says "go traverse the chain further and find me the same method" . super只是说“进一步遍历链条,找到我同样的方法”

For Base , we have two implementations of foo - that in Base and that in Gen . 对于Base ,我们有两个foo实现 - 在BaseGen The Gen one will be found first as the module was prepended. 在模块被预先安装之前,将首先找到第一Gen Therefore calling it on an instance of Base will call Gen#foo = [S] , which will search up the chain as well (via super ) = [from Base] . 因此,在Base的实例上调用它将调用Gen#foo = [S] ,它也将搜索链(通过super= [from Base]

For Derived , the module wasn't prepended, and we have inheritance. 对于Derived ,模块没有前置,我们有继承。 Therefore, the first found implementation is that in Derived = Sfrom Derived and super will search the rest of the chain that comes from Base (aka the above paragraph is played out) = [from Base]from Derived . 因此,第一个找到的实现是在Derived = Sfrom Derivedsuper将搜索来自Base的其余链(也就是上面的段落被播放) = [from Base]from Derived

For Derived2 , the module is prepended, so the method there will be found first = [S] . 对于Derived2 ,模块是前置的,因此首先会找到方法= [S] Then the super there will find the next foo in Derived2 = [Sfrom Derived] , and the super there will play out the situation for Base again = [[from Base]from Derived] . 然后super会在Derived2 = [Sfrom Derived]找到下一个foo ,而super将会再次显示Base的情况= [Sfrom Derived] [[from Base]from Derived]


EDIT: It seems that up until very recently, prepend would first search in the ancestors chain and add the module only if it is not already present (similarly to include ). 编辑:似乎直到最近, prepend将首先在祖先链中搜索并仅在模块尚未存在时添加模块(类似于include )。 To make it even more confusing, if you first create the parent, inherit from it, prepend in the child and then prepend in the parent, you will get the result of newer versions. 更令人困惑的是,如果您首先创建父级,继承它,在子级中添加前置,然后在父级中添加,您将获得更新版本的结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM