特殊类型的 eval

eval 相关的一些变体以名为 instance_evalmodule_evalclass_eval 方法的形式出现。可以从特定对象调用 instance_eval 方法,并且它提供对该对象的实例变量的访问。它可以用块或字符串调用:

instance_eval.rb
  1. class MyClass
  2. def initialize
  3. @aVar = "Hello world"
  4. end
  5. end
  6. ob = MyClass.new
  7. p( ob.instance_eval { @aVar } ) #=> "Hello world"
  8. p( ob.instance_eval( "@aVar" ) ) #=> "Hello world"

另一方面,eval 方法不能以这种方式从对象调用,因为它是 Object 的私有方法(而 instance_eval 是公有方法)。实际上,你可以通过将其名称(符号 :eval)发送到 public 方法来显式更改 eval 的可见性,尽管通常建议不要在基类中没有理由的去更改方法可见性!

严格地说,eval 是 Kernel 模块的一个方法,它是被混入到 Object 类中的。事实上,Kernel 模块提供了大多数可用作 Object 方法的函数。

你可以通过以这种方式添加到 Object 类定义来更改 eval 的可见性:

  1. class Object
  2. public :eval
  3. end

实际上,请记住,当你编写“独立”的代码时,你实际上是在 Object 的作用域内工作,只需输入此代码(没有类 Object 包装器)就会产生相同的效果:

  1. public :eval

现在你可以使用 eval 作为 ob 变量的方法:

  1. p( ob.eval( "@aVar" ) ) #=> "Hello world"

module_evalclass_eval 方法分别对模块和类而不是对象进行操作。例如,此代码将 xyz 方法添加到 X 模块(此处 xyz 在块中定义,并通过 define_method 作为接收对象的实例方法添加,这是 Module 类的方法);并将 abc 方法添加到 Y 类:

module_eval.rb
  1. module X
  2. end
  3. class Y
  4. @@x = 10
  5. include X
  6. end
  7. X::module_eval{ define_method(:xyz){ puts("hello" ) } }
  8. Y::class_eval{ define_method(:abc){ puts("hello, hello" ) } }
访问类和模块方法时,你可以使用作用域解析运算符 :: 或单个点。访问常量时,作用域解析运算符是必需的,访问方法时是可选的。

所以,现在作为 Y 实例的对象将有权访问 Y 类的 abc 方法和已混合到 Y 类中的 X 模块的 xyz 方法:

  1. ob = Y.new
  2. ob.xyz #=> "hello"
  3. ob.abc #=> "hello, hello"

尽管名称不同,但 module_evalclass_eval 在功能上是相同的,并且每个都可以与模块或类一起使用:

  1. X::class_eval{ define_method(:xyz2){ puts("hello again" ) } }
  2. Y::module_eval{ define_method(:abc2){ puts("hello, hello again" ) } }

你也可以以相同的方式将方法添加到 Ruby 的标准类中:

  1. String::class_eval{ define_method(:bye){ puts("goodbye" ) } }
  2. "Hello".bye #=> "goodbye"