我有一个模块和一个类.

module Dog
  def speak
    "woof"
  end
end

class Daschund; end

我创建了该类的两个单独的实例.

sausage = Daschund.new
saveloy = Daschund.new

如果我想添加Dog#woof作为我的两个新对象的实例方法,我可以通过两种方式来实现:

class << sausage
  include Dog
end
> sausage.speak
=> "woof"
saveloy.extend Dog
> saveloy.speak
=> "woof"

这两种方法是等价的吗?我知道第一个将模块的方法添加到对象的元类中.object#extend会做同样的事情吗?或者,它正在做一些略有不同的事情?有什么方法可以证明这一点吗?

推荐答案

根据Object的文档,使用模块扩展对象意味着将该模块包括到对象的单例类中:

extend:包括self的单例类中的给定模块.

判断这两个对象的ancestors个单例类确认了这一点:

sausage.singleton_class.ancestors
#=> [#<Class:#<Daschund:0x00007fa6af92e868>>, Dog, Daschund, Object, Kernel, BasicObject]

saveloy.singleton_class.ancestors
#=> [#<Class:#<Daschund:0x00007fa6af92e778>>, Dog, Daschund, Object, Kernel, BasicObject]

当然,实际的实现细节取决于您正在使用的Ruby实现和版本.对于MRI/YARV,您有rb_extend_object的定义为:

void
rb_extend_object(VALUE obj, VALUE module)
{
    rb_include_module(rb_singleton_class(obj), module);
}

Ruby相关问答推荐

Ruby错误-应为数组或字符串,已获取哈希

Ruby 中无法解释的撬动行为

数组到哈希:字数

Rspec: expectvsexpect什么区别?

在 MacOS Sierra 上使用 RMagick 2.16 的 ImageMagick 7 找不到 MagickWand.h

如何在没有 Web 服务器的情况下从命令行执行 ruby​​ 模板文件 (ERB)?

RSpec 是否有可能期望在两个表中发生变化?

在 RSpec-2.11 中使用隐含的 `subject` 和 `expect`

Python 是否在 Ruby 中进行类似于字符串 #{var}的变量插值?

Ruby 和您必须使用 OpenSSL 支持重新编译 Ruby 或更改 Gemfile 中的源代码

Jekyll - 找不到命令

在 Ruby 中将散列扁平化为字符串

跳过 Enumerable#collect 中的迭代

工厂女孩 - 目的是什么?

如何在 Ruby 中重命名文件?

从Ruby中的子类方法调用父类中的方法

Ruby总是Round Up

Ruby:捕获异常后继续循环

如何在 Ruby 中找到字符串中字符的索引?

传递哈希而不是方法参数