Ruby,为Module、Class或Object添加方法

polhcujo  于 5个月前  发布在  Ruby
关注(0)|答案(1)|浏览(71)

我在给Kernel添加方法以使它们全局可用时注意到一些奇怪的事情,这很有趣,我正在寻找一些文档或好的解释。
让我们看看代码:

文件:./demo.rb

# example 1

module Kernel
  def foo
    puts "I'm defined inside the module!"
  end
end

# example 2

module Bar
  def bar
    puts "I'm included! (bar)"
  end
end

Kernel.send :include, Bar

# example 3

module Baz
  def baz
    puts "I'm included! (baz)"
  end
end

module Kernel
  include Baz
end

字符串

然后,在bash和IRB中

$ irb -r ./demo.rb
> foo
# I'm defined inside the module!
> bar
# NameError: undefined local variable or method `bar' for main:Object
> baz
# NameError: undefined local variable or method `baz' for main:Object
>
> self.class.ancestors
# => [Object, Kernel, BasicObject] 
>
> include Kernel
>
> self.class.ancestors
# => [Object, Kernel, Baz, Bar, BasicObject]
>
> foo
# I'm defined inside the module!
> bar
# I'm included! (bar)
> baz
# I'm included! (baz)


foo按预期工作,并且可用于所有包含Kernel的对象。另一方面,barbaz不是立即可用的。
我想这是因为IRB(Object)的 evaluation context 已经包含了Kernel,而在模块B中包含模块A将不会“重新加载”之前包含的所有B。
好吧,这是完全合理的,实际上重新包含Kernel将添加其他两个方法。
那么,我的问题是:
1.为什么 openingKernel工作?(示例1)
1.如果 * 打开 * 模块的处理方式不同,为什么第三个例子不能正常工作?

t3psigkw

t3psigkw1#

当你在Ruby中调用foo.bar时会发生什么?类似这样:

foo.class.ancestors.each do |klass|
  if klass.public_instance_methods.include? :bar
    return klass.instance_method(:bar).bind(foo).call
  end
end
raise NameError

字符串
也就是说,Ruby会搜索所有的祖先来找到匹配的示例方法。
当你在Ruby中调用A.include B时会发生什么呢?类似这样:

B.ancestors.each do |mod|
  A.ancestors << mod unless A.ancestors.include? mod
end


B及其所有祖先都成为A的祖先。这两个行为解释了一切:
1.打开Kernel是可行的,因为它包含在Object中,因此是每个对象的祖先,这意味着它的方法(包括新方法)可以在任何对象上调用方法时搜索。
1.打开一个模块并没有区别对待。你的第二个和第三个例子实际上是一样的。它们都不起作用,因为Kernel的祖先只在它被包含时才被搜索,这是在你向它添加新的祖先之前。

相关问题