最近开始学习Ruby,在看到instance_eval和class_eval的用法时,觉得很是困惑,于是对此进行研究并整理一下。
首先,我们先定义一个Test类,该类包含一个类方法print_class_var和一个实例方法print_var。在print_class_var中,我们定义了一个属于Test类的实例变量var,而在print_var中,我们定义了一个属于Test类实例的实例变量var,这两个var是不一样的。然后我们创建Test类的一个实例test,然后并分别对Test类和test实例使用instance_eval和class_eval。
class Test def self.print_class_var puts @var end def print_var puts @var end end test = Test.new Test.instance_eval do def method1 @var = 1 puts "Class method" end end test.instance_eval do def method2 @var = 2 puts "Eigenclass method" end end Test.class_eval do def method3 @var = 3 puts "Instance method" end end
首先,我们对Test.instance_eval进行测试,分别调用Test.method1和test.method1。
Test.method1 #Class method Test.print_class_var #1 test.print_var # test.method1 #undefined method `method1' for #<Test:0x007fc5db945bd0> (NoMethodError)
可以看出,Test.method1能正常输出,而test.method1无法运行,并且在Test.method1执行之后,Test.print_class_var有输出结果,test.print_var没有输出结果,即Test.method1对Test类的实例变量进行了设置。
然后,我们对test.instance_eval进行测试,分别调用Test.method2和test.method2。
Test.method2 #undefined method `method2' for Test:Class (NoMethodError) test.method2 #Eigenclass method Test.print_class_var # test.print_var #2
可以看出,Test.method2无法运行,而test.method2能正常输出,并且在test.method2执行之后,Test.print_class_var没有输出结果,test.print_var有输出结果,即test.method2对test的实例变量进行了设置。
最后,我们对Test.class_eval进行测试,分别调用Test.method3和test.method3。
Test.method3 #undefined method `method3' for Test:Class (NoMethodError) test.method3 #Instance method Test.print_class_var # test.print_var #3
可以看出,Test.method3无法运行,而test.method3能正常输出,并且在Test.method3执行之后,Test.print_class_var没有输出结果,test.print_var有输出结果,即test.method3对test的实例变量进行了设置。
在做完上面3个测试之后,相信大家都很困惑,到底instance_eval和class_eval有什么区别呢?按照字面上的理解,instance_eval应该是用来创建实例方法,而class_eval应该是用来创建类方法。
但实际上,结果恰恰相反,instance_eval必须由instance来调用,可以用来定义单例方法(Eigenclass Method或者Singleton Method);而class_eval必须由class来调用,可以用来定义实例方法(Instance Method)。
对于Test.instance_eval的测试,由于Test类是Class类的一个实例,因此就定义了Test类的单例方法method1,而类方法属于单例方法,进而method1只会对Test类的实例变量进行操作。
对于test.instance_eval的测试,由于test是Test类的一个实例,因此就定义了test实例的单例方法method2,进而只会对test的实例变量进行操作。
对于Test.class_eval的测试,由于定义了Test类的实例方法method3,因此只会对test的实例变量进行操作。