class_eval,class_exec,module_eval和module_exec有什么区别?
我正在阅读Module
文档,但似乎无法理解它们之间的差异,应该在哪里使用。
eval
和exec
什么不同?
我将通过在您的问题中包含instance_{eval|exec}
来回答您的问题。
{instance|module|class}_{eval|exec}
所有变体都会改变当前的上下文 ,也就是self
:
class Array p self # prints "Array" 43.instance_eval{ p self } # prints "43" end
现在的差异。 eval
版本接受一个string或一个块,而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}
略有不同,因为它们改变了当前打开的类(即,什么会受到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
本身。
当然, instance_{eval|exec}
可以在任何Ruby对象(包括模块)上使用,而{class|module}_*
仅在Module
(和Classes
)
要首先回答你的最后一个问题,eval(所有变体)与exec 完全不同。 exec $command
将启动一个新的进程来运行你指定的命令,然后在完成时退出。
class_eval
和module_eval
有能力重新定义类和模块 – 即使是你自己没有写的。 例如,您可以使用class eval来添加一个不存在的新方法。
Fixnum.class_eval { def number; self; end } 7.number # returns '7'
class_eval
可以用来添加实例方法,而instance_eval
可以用来添加类方法(是的,这部分是非常混乱的)。 类方法就像Thing.foo
– 你真的在Thing
类中调用foo
方法。 一个实例方法就像上面的例子,使用class_eval
我已经给每个Fixnum
实例添加了一个number
方法。
好的,这是*_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中有很多工具,你可以使用这些工具来解决很多问题。 很多时候,你可能甚至不需要,除非你想猴子补丁一些你使用的库已经定义的类(虽然这打开了一整jar蠕虫)。 尝试在irb中与他们玩耍,看看你更容易find。 我个人并不像*_exec
方法那样使用*_eval
方法,但这是我个人的偏好。