阅读时要区分:类、类的实例对象、类对象! 如何理解 ruby 中的元编程?yehuda 说搞定一个 self 就 ok 了,我看了,还是有点晕晕乎乎,结合学习 ruby 元编程的那本书,我觉得,关键是搞懂 def 这个关键字。 def 的定义有两种情况(方法的前面有没有前缀:接收者): 一 def method_name 二 def x.method_name
第一种情况, def method_name p "ok" end 会在当前类(用 CurrentClass 表示当前类)中定义一个实例方法(所谓类的实例方法,虽然是定义在存放在类中,但类自己不能调用,只能 new 一个实例才能调用)即: CurrentClass.method_name #=> undefined CurrentClass.new.method_name #=> ok
第二种情况, def x.method_name end 会定义一个单件方法,即属于 x 的单件方法,x 是什么,x 就是对象,在这里不管是 myob、MyClass、self 还是什么其他的什么对象,可以是一般对象,也可以是类对象,总之是一个对象。我们知道方法只能放在类中,所以这个单件方法会放在 x 的单件类(大家都知道这个单件类藏起来了)中。
class Myclass def method1 p "method1" end def self.method2 p "method2" end
def Myclass.method3 p "method3" end class<<Myclass def method4 p "method4" end end class<<self def method5 p "method5" end end end
上面这几个方法定义中,method1,method2 和 method3 无需解释,method4 和 method5 要琢磨一下,这两个方法属于第一种情况,没有前缀,是一个普通的实例方法,关键是看当前类是什么,还是 Myclass 吗?
class<<X 我这个位置很特殊 end 就是进入 X 的单件类的作用域中,“我这个位置很特殊”这里的当前类就是 X 的单件类。补充一句,X 是个对象。 而上述定义 method4 和 method5 的地方正好是位于 Myclass 类对象的单件类之内,而不是 Myclass 类对象本身,所以 method4 和 method5 也就很好理解了,这两个方法的接受者是类(Myclass 类对象的单件类)的实例对象(也就是 Myclass 类对象), 所以这里定义的两个方法恰好是 MyClass 的类方法。
实在是拗口,不知道我有没有说清楚。 总结一句话的话,要看 def 前面有没有内容,如果没有,就是纯真的方法定义,要看当前类是什么,最终就是是这个当前类的实例方法;如果有前缀 x.,方法就定义在 x 的单件类中。
再来看两种情况, MyClass.class_eval do def method6 p "method6" end end
MyClass.instance_eval do def method7 p "method7" end end
注意,上述两种情况都属于没有前缀的,所以会纠结在当前类什么这个问题上。 class_eval 会修改当前类,在这里,当前类就是 Myclass,所以 method 的存放位置就在 Myclass 中,这个方法就一个实例方法。 instance_eval 呢?我们知道这个方法会修改当前对象(即 self),但是会修改当前类吗,答案是会!在 MyClass.instance_eval 中, 当前类是 MyClass 的单件类,所以呢,这个方法是一个类方法。
参考文献: http://yehudakatz.com/2009/11/15/metaprogramming-in-ruby-its-all-about-the-self/ 另加: http://yugui.jp/articles/846(这篇和我说的意思比较接近)
补充一点:许多情况下,当前类和当前对象(即 self)是一致的,但也有时候也会有差别: 例如: class MyClass def method1 end def self.method2 end end 在这里,当前类和当前对象是一致的,即都是 MyClass,定义方法时声明接收者的话,方法就会定义在当前对象的单件类中,不声明接收者的话,方法就会定义在当前类中。
但在 X.instance_eval 中,当前对象和当前类是不一样的,当前对象是 X,而当前类则是 X 的单件类,从而导致: class MyClass end MyClass.instance_eval do def method1 end def self.method2 end end 中,method1 和 method 都是 MyClass 的类方法。。