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. 在类定义的上下文中对字符串求值
  2. 为匿名类(但不包含单例类)打开类定义(也就是说在不知道类的名字的情况就打开一个类)
  3. 获取外围作用域中变量的访问权
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()其实是扁平化作用域。