转载请包含源链接:http://blog.csdn.net/wu4long/article/details/42191929
ruby语言中,尤其是meta programming中,很多初学者或者有一定经验者,都未必能搞的清楚instance_eval和class_eval以及self和current class。
首先我们要明确一点,在任何时刻,ruby都会一直追踪当前的对象(self)和当前的类(current class)。只有把握这两个东西,所有的元编程的手法都可以理解它里面的含义。
在ruby metaprogramming一书中,是这样来描述的instance_eval和class_eval:这两个method都会更改当前的对象(self)和当前的类(current class)。obj.instance_eval 会将当前的self 改为obj, current class为obj的eigenclass(或者说singleton class,以#obj来表示obj的eigenclass)。 而 klass.class_eval 会将当前的self改为klass, current class为 klass。
从理论来来说,上面解释的是很正确的也很到位的。但为什么很多人,就算阅读了上面的说明,一时半会也无法真正理解一些实际的代码(包括我本人在内)。我感觉是没有人真正把self和current class的一些变化的情况给一个完整的总结,只有完整的总结出来,初学者才能有纲可循。
self和current class的变更情况:
1. module 或者 class 定义中, self 和 current class 都会变更为当前的module。
class A
# self is A, current class is A
end
2. klass.class_eval 也是将self和current class都会变更成当前的klass.
A.class_eval do
# self is A, current class is A.
end
3. 显式打开eigenclass, 此时会将当前的#obj(obj的eigenclass)变更为self, #obj(obj的eigenclass)为current class.
class << A
# self is #A, current class is #A.
end
4. obj.instance_eval 也会将当前的obj变更为self, #obj(obj的eigenclass)为current class.
A.instance_eval do
# self is A, current class is #A
end
基本上搞清楚了上面的这4条,很多代码基本都知道它的所以然了。
5. def obj.singleton_method 此时self和current class 会变更吗?
从def这个关键字来看,它只是在当前的current class定义实例方法。不存在执行代码,因此self不会变更的,current class也不会变更的。 如果加上obj.之后,#obj会变更当前的current class?如果只是定义一个方法而言,从结果来看,确实是在#obj增加了实例方法,那此时current class到底变了没? 我们暂时不回答,你可以假设它变了。
6. obj.method 此时会把self变更为obj, 但current class不会变更。
7. 在方法中method_a 再通过def 来定义新的方法 method_a_inner,此时method_a_inner会将定义method_a时的current class进行bind,而不是和调用方法method_a此时的current class相关。
我们通过下面的情况来分析。
1)
class A
def self.singleton_a
def h_a
"h_a self is #{self}"
end
end
end
2)
class A
class << A
def singleton_a
def h_a
"h_a self is #{self}"
end
end
end
end
3)
class A
end
def A.singleton_a
def h_a
"h_a self is #{self}"
end
end
上面三种情况,都是在A的eigenclass(#A)定义了方法singleton_a。 如果我们调用 A.singleton_a 之后,方法 h_a 究竟是 A的实例方法还是 #A(A的eigenclass)的实例方法呢? A.singleton_a
A.respond_to?(:h_a) # true or false ????????????????
A.new.respond_to?(:h_a) # true or false ??????????????????
Object.new.respond_to?(:h_a) #true or false ????????????
1) 第一种情况:
A.singleton_a
A.respond_to?(:h_a) # false
A.new.respond_to?(:h_a) # true
Object.new.respond_to?(:h_a) # false
2)第二种情况:
A.singleton_a
A.respond_to?(:h_a) # true
A.new.respond_to?(:h_a) # false
Object.new.respond_to?(:h_a) # false
3) 第三种情况A.singleton_a
A.respond_to?(:h_a) # true
A.new.respond_to?(:h_a) # true
Object.new.respond_to?(:h_a) # true
同样调用的是A.singleton_a的方法,为何三种情况的结果都是截然不同呢?
从这里看出,obj.method 时,是不会变更current class的,如果变更的话,那同样的对象,调用同样的方法应该current class是一致的。那为何当前的current class有差异呢?估计和语言本身设计相关。def 只和词法相关,由当前的定义时的current class进行绑定,而和方法调用时的current class无关。
第一种情况,current class为A,第二种情况 current class 为#A, 第三种情况current class为Object。
由上面的结论,我们知道了,第一种情况下 def A.singleton_a 的current class为A,第三种情况下 current class为Object,从而我们可以反向推断,上面第5条提出的问题,def A.singleton_a 不会变更current class,只是语言自身强行将此方法放入#A中。而在此方法中再定义方法时,还是把当前的current class也bind到一起了。从而我们才能解释上面的情况。
至此,我们基本都解释了current class 和 self一些变更的情况。
分析一个简单而又容易被搞混淆的实例,
A.instance_eval do
define_method(:t1) do
puts "t1"
def t4
end
end
def t2
"t2"
def t3
end
end
end
根据上面的说法,A.instance_eval 将当前的self变更为A,current class为#A(A的eigenclass)。
我们说过,def 是根据当前的词法分析上的current class,在这边来说,就是#A,因此 t2 是#A的实例方法。而define_method是调用于self上的,也就是A,所以A有个实例方法 t1.
即:A.respond_to?(:t2) # true
A.new.respond_to?(:t1) # true
调用 A.t2, A.new.t1之后, t3 和 t4 分别是那个class的实例方法呢?从我们刚才说的,在定义def t3,t4时,词法分析当前的current class是#A,从而t3和t4也就是#A的实例方法。而测试的结果也证实了这一点。
A.respond_to?(:t3) #true
A.respond_to?(:t4) #true
A.new.respond_to?(:t3) # false
A.new.respond_to?(:t4) # false
至此我们基本上分析完了,self和current class的变更情况。并且也把def 和 define_method的进行了区分。define_method是在self(这个class)上添加的一个实例方法,而def是在词法分析中的current class类中定义的一个实例方法。 而有些时候,self和current class都是一样的,比如: class A; end 和 A.class_eval 中,此时 define_method和def就产生一样的效果了,从而有些书或者资料就说,这两者是等同的,或者说define_method只是破除了scope的限制,可以引用当前的局部变量了。但真正他们的区别就没人来说的透彻,从而大家都是一知半解。
当然,估计我有些地方说的不准确,不全面,也希望大家指正。只是按照我目前的说法,我能把我现在遇到的问题都可以迎刃而解。如果后面遇到了不能解释的,我们再进行具体分析和更正。