Ruby中方法&常量的查找路径

    xiaoxiao2021-03-25  84

    Ruby中方法继承、方法查找路径、constant查找路径 @(Ruby)


    继承类方法和实例方法方法调用路径常量查找路径总结

    继承类方法和实例方法

    class A def method1 end def self.method2 end end class B < A def method3 end def self.method4 end end b = B.new b.method1 #nil b.method3 #nil B.method2 #nil

    可以看到,B不仅继承了A的实例方法,还继承了A的 singleton方法。

    方法调用路径

    module Include def call(level) puts "#{level} include" super(level + 1) rescue nil end end module Prepend def call(level) puts "#{level} prepend" super(level + 1) rescue nil end end module Extend def call(level) puts "#{level} extend" super(level + 1) rescue nil end end class Super def call(level) puts "#{level} super" super(level + 1) rescue nil end end class Klass < Super include Include prepend Prepend def call(level) puts "#{level} klass" super(level + 1) rescue nil end end thing = Klass.new def thing.call(level) puts "#{level} singleton" super(level + 1) rescue nil end thing.extend(Extend) thing.call(1)

    输出结果为:

    1 singleton 2 extend 3 prepend 4 klass 5 include 6 super

    此时继承链为

    > Klass.ancestors => [Prepend, Klass, Include, Super, Object, Kernel, BasicObject] > thing.singleton_class.ancestors => [#<Class:#<Klass:0x007fe42a89d5a8>>, Extend, Prepend, Klass, Include, Super, Object, Kernel, BasicObject]

    可以看到实例 thing 上的方法的寻找路径是根据它的单例类的继承链来决定的。

    常量查找路径

    常量的查找是按照下面的规则进行的: - 首先查找Module.nesting路径,这个Module.nesting跟方法的调用没关系,而是根据方法定义的位置而确定的, - 否则查找Module.nesting.first.ancestors - 如果Module.nesting.first是nil或者module,查找Object.ancestors

    1、通过Module.nesting查找,路径为[A].

    class A def get_c C end end class B < A module C; end end B.new.get_c => "NameError: uninitialized constant A::C"

    可以看到,虽然B中定义了常量C,但是get_c没有从B中开始查找,而是从自己被定义的模块A开始查找,找不到而报错。

    2、Module.nesting找不到,则从Module.nesting.first.ancestors开始查找,此时为会去A的父类AParent中查找。

    class AParent C = 'AParent' end class A < AParent def get_c C end end class B < A C = 'in B' end B.new.get_c => "AParent"

    3、 如果Module.nesting.first是nil或者module类型,则从Object查找:

    class AParent C = 'AParent' end class A < AParent module M def get_c C end end end class B extend A::M end B.get_c => "NameError: uninitialized constant A::M::C"

    因为Object中没有C常量,因此会报错,我们为Object加上常量C,此时运行结果如下:

    class Object C = 'In Object' end class AParent C = 'AParent' end class A < AParent module M def get_c C end end end class B extend A::M end B.get_c => "In Object"

    总结

    这是一段Ruby内建的常量查找代码,使用了 binding.eval ,不管在任何地方调用,都能 保证查找的起点都是从它被定义的地方。

    class Binding def const(name) eval <<-CODE, FILE, LINE + 1 modules = Module.nesting + (Module.nesting.first || Object).ancestors modules += Object.ancestors if Module.nesting.first.class == Module found = nil modules.detect do |mod| found = mod.const_get(#{name.inspect}, false) rescue nil end found or const_missing(#{name.inspect}) CODE end end

    参考文章: https://cirw.in/blog/constant-lookup.html http://pascalbetz.github.io/ruby/2016/03/14/lookup-path/

    转载请注明原文地址: https://ju.6miu.com/read-22163.html

    最新回复(0)