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