在使用 define_method 方法的过程中,经常会感觉到疑惑,因此下面做一个总结。
#下面的代码直接在类中使用define_method方法定义:test的类
class Test
define_method :test do
puts "this is the define_method demo"
end
end
obj = Test.new
obj.test #=>"this is the define_method demo"
最初的疑惑是为什么可以在类中直接使用 define_method 方法,然后以为的解释是实现了 module_funciton 的功能,module_functon 主要实现了下面的两个功能,代码如下所示:
#模块名.方法名直接调用
Kernel.puts "this is the test"
#类似于关键字在程序的任何部分进行调用
puts "this is the test"
但是其实,上面的解释是合理,因为在 define_method 不能实现上面的两个功能,即不能实现 Module.define_method 功能,也不能随意在程序块内直接使用 define_method 功能。
irb: Module.private_instance_method.grep(/define_method/)
irb: [:define_method]
class Module
def demo
puts "this is the demo"
end
private :demo
end
class Test
#self.demo是要报错的,但是如果没有private标示符,self和self.demo都是可以的。
demo
end
可以看出 module_function
和 private
都是 Module
类的实例方法。但是:
Module.methods.grep /^priva/
=> [:private_instance_methods,
:private_constant,
:private_method_defined?,
:private_class_method,
:private_methods]
Module.methods.grep /^mod/
=> [:module_exec, :module_eval]
其中原因暂时不知,若知晓,勿忘告知。
很难说用了 Module 类中那些方法实现了 module_funciton 功能,因为 module_funciton 是用 C 语言来写的,并没有调用其他方法。基本上这个 C 函数就是检查参数,查找参数里的方法名在 module 中是否存在。然后用这一句
rb_method_entry_set(rb_singleton_class(module), id, me, METHOD_VISI_PUBLIC);
将这个 instance_method 注册为 class_method。所以说 define_method 和 instance_method 两者互相独立,不存在相互调用关系,而且两者的功能完全不一样。define_method 是直接定义方法,而 module_funciton 是将本来在 module 里的 instance_method 绑定为 class_method,好通过 Module 名直接调用。
#5 楼 @reyesyang 写出这句话的时候,我心里也是非常疑虑,不过似乎只有人为它实现了 module function 的功能,才能解释它在这里的用法,被对象隐式调用。
irb(main):001:0> Module.class
=> Class
irb(main):002:0> Class.class
=> Class
irb(main):003:0> Class.superclass
=> Module
irb(main):004:0> Module.superclass
=> Object
irb(main):005:0> Class.superclass.superclass
#3 楼 @qinfanpeng 请教别的问题。好奇的问下,为啥我在http://api.rubyonrails.org/ 中搜索 mo 跟你这个结果不一致呀。是不是有啥门道?而且我的下面也没有实例方法。谢谢。
#3 楼 @qinfanpeng https://l.ruby-china.com/photo/2016/096485cd774eaad81eb5e1f32a0f3b3d.png 请教这个截图里面的内容是咋出来的。谢谢。
#13 楼 @imconfused 回过头来看,有点明白了,但是这里的话,Modulel 类是 Class 类的父类,那么 Module 类的实例方法被 Class 类继承,因此 Module 类的实例方法可以作为普通类的类方法进行直接调用。但是,Object 是 Module 的父类,Module 是 Class 的父类,那么 Object 是 Class 类的间接父类,那么为什么 Object 类的实例方法没有成为普通类的类方法? 也请教下您@qinfanpeng
#23 楼 @runup 自己用irb
多试试看,对于ruby
的继承链有点多的了解了以后再思考这些比较细致的问题,多看看Ruby元编程
,我自己也前前后后翻了 3 遍,但是对于有些概念还是摸不透,也要用irb
来验证自己的想法
irb(main):033:0> BasicObject.instance_methods.join ','
"==,equal?,!,!=,instance_eval,instance_exec,__send__,__id__"
irb(main):034:0> Object.instance_methods(false).join ','
"methods,private_methods,protected_methods,public_methods,singleton_methods"
irb(main):035:0> Kernel.instance_methods(false).join ','
"nil?,===,=~,!~,eql?,hash,<=>,class,singleton_class,clone,dup,itself,taint,tainted?,untaint,untrust,untrusted?,trust,freeze,frozen?,to_s,inspect,methods,singleton_methods,protected_methods,private_methods,public_methods,instance_variables,instance_variable_get,instance_variable_set,instance_variable_defined?,remove_instance_variable,instance_of?,kind_of?,is_a?,tap,send,public_send,respond_to?,extend,display,method,public_method,singleton_method,define_singleton_method,object_id,to_enum,enum_for,ai,awesome_inspect,awesome_print"
irb(main):036:0> Class.instance_methods(false).join ','
"allocate,new,superclass,instance_methods,private_instance_methods,protected_instance_methods,public_instance_methods"
irb(main):037:0> Module.instance_methods(false).join ','
"freeze,===,==,<=>,<,<=,>,>=,to_s,inspect,included_modules,include?,name,ancestors,instance_methods,public_instance_methods,protected_instance_methods,private_instance_methods,constants,const_get,const_set,const_defined?,const_missing,class_variables,remove_class_variable,class_variable_get,class_variable_set,class_variable_defined?,public_constant,private_constant,singleton_class?,include,prepend,module_exec,class_exec,module_eval,class_eval,method_defined?,public_method_defined?,private_method_defined?,protected_method_defined?,public_class_method,private_class_method,autoload,autoload?,instance_method,public_instance_method"
irb(main):038:0> class A
irb(main):039:1> end
nil
irb(main):040:0> A.ancestors
[
[0] A < Object,
[1] Object < BasicObject,
[2] Kernel,
[3] BasicObject
]