2
Foo = Module.new

class MyClass
  include Foo
end

当一个模块包含在一个类中时,会创建一个匿名代理类并将其设置为 MyClass 的超类。

MyClass.ancestors => [MyClass, Foo, ...]

但是当一个模块被扩展时,内部会发生什么呢?Ruby 是如何处理这个问题的?

4

4 回答 4

5

我想你问的是Object#extend

因此extend,我可以任何模块的方法包含到该对象中。例如,我有一个名为 HelperModule 的模块:

module HelperModule
  def foo
    puts "from module helper"
  end
end

使用示例 1:

obj = Object.new
obj.extend HelperModule
obj.foo                  # => "from module helper"

使用示例 2:

class MyClass
  extend HelperModule
end
MyClass.foo              # => "from module helper"

在内部,根据元编程 Ruby

Object#extend() 只是一个快捷方式,它在接收器的特征类中包含一个模块。


ruby 方法调用的简要说明:

  1. 找到对象的类,看看那里定义的方法
  2. 找到那个类的超类,看看那里定义的方法

obj 
 |
 | class
 |                     superclass                  superclass
 --->  ObjectClass  --------------> SuperClass1 --------------> SuperClass2 ....

关于eigenclass和方法调用路径的详细解释,请参考这本很棒的书Metaprogramming Ruby

谢谢

于 2013-07-26T05:09:36.010 回答
0

当一个模块在 Ruby 中扩展时,内部会发生什么?

当一个模块M包含在一个类C中时,会创建一个匿名代理类⟦M′⟧(称为包含类),使其方法表指针指向M的方法表。(对于常量表和模块变量也是如此。)⟦M′⟧的超类设置为C的超类,C的超类设置为⟦M′⟧

此外,如果M包含其他模块,则递归应用该过程。

实际上,这只是默认行为。真正发生的是include调用M.append_features(C),您可以通过覆盖该方法来自定义所有这些行为。

我发现Rubinius的源代码Module#append_features非常易读。

于 2013-02-12T14:15:40.577 回答
0

obj.extend SomeModuleobj.eigenclass.include SomeModule注意:这只是伪代码,但你会明白的......)。

于 2013-07-26T07:34:36.877 回答
0

简而言之,Ruby 的include方法将模拟继承:它将允许包含类访问被包含模块的实例方法、变量和常量,就好像它们已在包含类本身中定义一样。正如您所提到的,Ruby 通过创建一个匿名代理类(称为 eigenclass 或单例类)来做到这一点,它引用包含的模块,并将其作为直接超类插入到包含类的祖先中。这就是 Ruby 中所谓的mixin

extend但是,使用 与单例类交互更多一点:

module Foo
  def baz
    'baz'
  end
end

module Blah
  def blah
    'blah'
  end
end

class Bar
  extend Foo
  include Blah
  def self.magic
    'magic'
  end
end

扩展FooBar说是相同的(一直到它在 C 中的实现)

Bar.singleton_class.send( :include, Foo )

这意味着它是Bar其中的方法和常量Foo本质上嵌入的单例类,因此Bar作为其单例类的实例的 class 将继承这个新功能作为所谓的“类”方法。在 Ruby 中,模块和类只能有实例方法,因此“类”方法的创建实际上只是在类的单例类中创建一个实例方法,以这种方式被继承。

Bar.baz
# => 'baz'
Bar.magic
# => 'magic'
Bar.blah
# => NoMethodError
Bar.new.baz
# => NoMethodError
Bar.new.magic
# => NoMethodError
Bar.new.blah
# => 'blah'

在这里您可以看到 和 的行为include差异extend。要验证他们关于类祖先的行为,您可以询问其祖先Bar及其单例类,它会告诉您模块Blah是 的直接父级Bar,但模块Foo是单例类的直接父级。

Bar.ancestors
# => [Bar, Blah, Object, Kernel, BasicObject]
Bar.singleton_class.ancestors
# => [Foo, Class, Module, Object, Kernel, BasicObject]

从这些结果中,您可以看到包含如何模拟包含类中的继承,以及扩展如何仅包含在单例类中。

您可以尝试查看我不久前为这个问题得到的答案;include他们在解释和extend功能 的行为方面做得非常出色。这篇文章也帮助我理解了他们的不同之处。

于 2013-07-26T14:25:34.357 回答