class_eval,class_exec,module_eval和module_exec之间有什么区别?

时间:2012-01-29 23:20:38

标签: ruby

我正在阅读Module文档,但似乎无法理解它们之间的区别,哪些应该在哪里使用。

evalexec有什么不同?

2 个答案:

答案 0 :(得分:83)

通过在问题中加入instance_{eval|exec},我会回答一些问题。

{instance|module|class}_{eval|exec}的所有变体都会更改当前上下文,即self的值:

class Array
  p self                     # prints "Array"
  43.instance_eval{ p self } # prints "43"
end

现在是差异。 eval版本接受字符串或块,而exec版本只接受块但允许您将参数传递给它:

def example(&block)
  42.instance_exec("Hello", &block)
end
example{|mess| p mess, self } # Prints "Hello" then "42"

eval版本不允许传递参数。它提供了self作为第一个参数,尽管我无法想到它的用途。

最后,module_{eval|exec}与相应的class_{eval|exec}相同,但它们与instance_{eval|exec}略有不同,因为它们会更改当前打开的类(即受{影响的内容) {1}})以不同的方式:

def

因此String.instance_eval{ def foo; end } Integer.class_eval { def bar; end } String.method_defined?(:foo) # => false String.singleton_methods.include?(:foo) # => true Integer.method_defined?(:bar) # => true 会打开obj.instance_{eval|exec}的单例类,而obj会打开mod.{class|module}_{eval|exec}本身。

当然,mod可用于任何Ruby对象(包括模块),而instance_{eval|exec}仅适用于{class|module}_*(因此Module

答案 1 :(得分:6)

首先回答你的上一个问题,eval(在所有变体中)完全与exec不同。 exec $command将启动一个新进程来运行您指定的命令,然后在完成时退出。

class_evalmodule_eval有权重新定义类和模块 - 即使是那些你自己没有写过的东西。例如,您可以使用类eval添加不存在的新方法。

Fixnum.class_eval { def number; self; end }
7.number # returns '7'

class_eval可用于添加实例方法,instance_eval可用于添加类方法(是的,该部分非常混乱)。类方法类似于Thing.foo - 您实际上是在foo类上调用Thing方法。实例方法与上面的示例类似,使用class_eval我已为number的每个实例添加了Fixnum方法。

好的,那就是*_eval类方法。 exec方法类似,但它们允许您查看类并执行代码块,就好像它被定义为该类的方法一样。也许你有一个看起来像这样的课程:

class Foo
  @@secret = 'secret key'
  @@protected = 'some secret value'
  def protected(key)
    if key == @@secret
       return @@protected
    end
  end
end

如果您知道正确的密钥,则类Foo只是一个秘密值的包装。但是,你可以通过在类的上下文中执行一个块来欺骗类给你秘密,如下所示:

Foo.class_exec { @@secret = 'i'm a hacker' }
Foo.protected('i'm a hacker') #returns the value of @@protected because we overwrote @@secret

一般来说,使用ruby中的许多工具,您可以使用其中任何一个来解决许多问题。很多时候你甚至可能根本不需要,除非你想要修补你使用的某个类定义的类(尽管这会打开一大堆蠕虫)。尝试用irb玩它们,看看哪个更容易找到。我个人不像*_exec方法那样使用*_eval方法,但这是我个人的偏好。