猴子补丁vs class_eval?

class String def hello "world" end end String.class_eval { def world "hello" end } "a".world => "hello" "b".hello => "world" 

他们似乎做了同样的事情 – 向现有类添加方法。 那有什么区别?

使用class_eval您可以做更多动态的事情:

 >> met = "hello" #=> "hello" >> String.class_eval "def #{met} ; 'hello' ; end" #=> nil >> "foo".hello #=> "hello" 

class_eval在概念上做类重新打开(或猴子修补)。 主要是语法差异。 如果将字符串传递给class_eval (如Michael的示例中所示),则字符串内部的语法与class String; ... end语法大致相同class String; ... end class String; ... end 如果传递block: String.class_eval { ... }则比较如下:

  • 在class_eval块内部外部局部变量是可见的
  • 内部重新打开的类外部局部变量是不可见的
  • 在class_eval内部,您不能将作用域的常量和类变量分配给该类
  • 你可以在里面重新上课

知道其他差异会很有趣

其他答案都很好。 想要添加的是,当您希望引用类不是通过常量或修补特定对象时,可以使用class_eval

例如

 huh = String class huh end SyntaxError: (eval):2: class/module name must be CONSTANT huh.class_eval <<-eof def mamma puts :papa end eof "asdff".mamma => papa 

您可以使用class_eval来修补特定对象,而不会影响整个根类。

 obj = "asd" obj.singleton_class.class_eval <<-eof def asd puts "gah" end undef_method :some_method 

以上内容与:

 class << obj ... end 

通过某种用法, instance_eval会有稍微不同的行为。

我发现这个问题和答案很有趣: 如何在方法中修补ruby类

还有关于instance_eval vs class_eval问题,但我没有方便的链接。