ruby中的instance_eval,class_eval,eval
ruby具有在运行时执行以字符串形式保存的代码的功能设施,eval族方法 。包括Kernel#eval,Object#instance_eval,Module#class_eval。
Kernel#eval
它是最直接的方法
如下:
1 p eval("2+2") 2 3 eval("def m;p 'hello world';end") 4 eval("m")
输出
4
"hello world"
eval强大而危险,有代码注入之类的危险,尽管ruby中有一个全局变量$SAFE来控制它,但最好还是不要用它。
Object#instance_eval
该方法将self变为instance_eval调用的接收者,对字符串或者代码块求值。
如下:
1 p self 2 3 a = [] 4 a.instance_eval {p self}
输出
main
[]
instance_eval常常用于访问其他对象的私有数据--特别是实例变量
如下:
1 class C 2 def initialize 3 @x = 1 4 end 5 end 6 7 c = C.new 8 c.instance_eval {puts @x}
输出
1
instance_eval也可以接受字符串,访问局部变量。
如下:
1 arr = ['a','b','c'] 2 ch = 'd' 3 arr.instance_eval "self[1] = ch" 4 p arr
输出
["a", "d", "c"]
instance_eval中定义方法,则会是个单例方法
如下:
1 obj = Object.new 2 3 obj.instance_eval do 4 def method 5 p "hello world" 6 end 7 end 8 p obj.singleton_methods
输出
[:method]
Module#class_eval
class_eval和module_eval是一样的。它可以进入类定义体内。
1 C = Class.new 2 C.class_eval do 3 def method 4 p "hello world" 5 end 6 end 7 c = C.new 8 c.method
输出
"hello world"
class_eval可以做一些class关键字不能做的事
- 在类定义的上下文中对字符串求值
- 为匿名类(但不包含单例类)打开类定义(也就是说在不知道类的名字的情况就打开一个类)
- 获取外围作用域中变量的访问权
1 def add_method_to(a_class) 2 a_class.class_eval do 3 def method ; p "hello world";end 4 end 5 end 6 add_method_to String 7 "abc".method
输出
"hello world"
1 var = "hello world" 2 3 class C 4 end 5 C.instance_eval {puts var}
输出
"hello world"
总结:instance_eval()方法仅仅会改变self ,而 class_eval()会同时修改self和当前类,通过修改当前类,class_eval()实际上是重新打开了该类,就像class关键字一样。
Module#class_eval()比class关键字更加灵活,class关键字只能使用常量,而class_eval()可以对任何代表类的变量使用class_eval()方法。class会打开一个新的作用域,但是class_eval()其实是扁平化作用域。