深入理解 Ruby 中的 instance_eval 和 class_eval

Ruby 通过 eval 可以将字符串当做代码来执行,而执行环境是通过 binding 进行绑定的;除此之外,Ruby 还有另外两个方法:instance_eval 和 class_eval, 用来执行代码块。后者的执行上下文与调用者相关。

instance_eval: 调用者必须是实例 instance;
class_eval: 调用者必须是类 class;

下面的例子帮助识别两个方法的区别:

[1] pry(main)> class Hello
[1] pry(main)*   def self.say_c
[1] pry(main)*     puts "Hello self.say"
[1] pry(main)*   end  
[1] pry(main)*   def say_i
[1] pry(main)*     puts "Hello lsay"
[1] pry(main)*   end  
[1] pry(main)* end  

在 pry 交互环境中,先定义一个 Hello 类,有两个方法,一个类方法 say_c, 一个实例方法 say_i。

[2] pry(main)> ls Hello
Hello.methods: say_c
Hello#methods: say_i

分别通过 Hello.instance_eval 和 Hello.class_eval 定义两个新方法: foo_i, foo_c

[4] pry(main)> Hello.instance_eval do 
[4] pry(main)*   def fool_i
[4] pry(main)*     puts "Hello instance_eval foo_i"
[4] pry(main)*   end  
[4] pry(main)* end  
=> nil
[5] pry(main)> Hello.class_eval do
[5] pry(main)*   def foo_c
[5] pry(main)*     puts "Hello class_eval foo_c"
[5] pry(main)*   end  
[5] pry(main)* end  
=> nil

查看 Hello 的方法变化:

[6] pry(main)> ls Hello
Hello.methods: foo_i  say_c
Hello#methods: foo_c  say_i

有上面的结果可见,instance_eval 代码块中定义的方法变成了 Hello 的类方法,而 class_eval 代码块中
定义的方法称为了 Hello 类的实例方法。这里有点绕,跟语义上边理解起来的相反。

[7] pry(main)> h1 = Hello.new
=> #<Hello:0x007ff978fc5458>
[8] pry(main)> h1.instance_eval do 
[8] pry(main)*   def cry
[8] pry(main)*     puts "h1 cry"
[8] pry(main)*   end  
[8] pry(main)* end  
=> nil
[9] pry(main)> ls h1
Hello#methods: foo_c  say_i
self.methods: cry

h1 为 Hello 类的实例,h1.instance_eval 中定义的方法成为了 h1 的单例方法(类似于java中类的静态方法),也就是说只有 h1 能够调用的方法。

总结:

  • h1 不能调用 class_eval;
  • h1 调用 instance_eval 的作用域为对象实例本身;其中定义的方法为单例方法;
  • Hello 调用 class_eval 的作用域为类本身;其中定义的方法为实例方法;
  • Hello 调用 instance_eval 的作用域为 class 对象,其中定义的方法为类方法;

猜你喜欢

转载自blog.csdn.net/antony1776/article/details/77869158