ruby中的instance_eval,class_eval,eval

寻技术 Ruby编程 2023年12月30日 160

   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关键字不能做的事

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

 

关闭

用微信“扫一扫”