2012-02-22 71 views
12
class String 
    def hello 
    "world" 
    end 
end 

String.class_eval { 
    def world 
    "hello" 
    end 
} 

"a".world 
=> "hello" 
"b".hello 
=> "world" 

他们似乎做同样的事情 - 添加一个方法到现有的类。那有什么区别?猴子补丁vs class_eval?

回答

12

随着class_eval你可以做更多的事情动态:

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

class_eval做概念类重新开放(或猴子修补)。大多数语法差异。如果您将字符串传递给class_eval(如Michael的示例),则在字符串中的语法大多与class String; ... end中的语法相同。如果你传递块:String.class_eval { ... }它比较如下:

  • 内class_eval块外局部变量是可见的
  • 重新开放类外的局部变量中是不可见的
  • 内class_eval您不能分配范围的常量和类变量对类
  • 重新打开类中,你可以

这将是有趣的,知道其他差异

0

其他答案都很好。想要添加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修补特定对象不affectin整根类。

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

以上是一样的:

class << obj 
    ... 
end 

instance_eval将不得不使用一些稍有不同的行为。

我觉得这个问题和答案有趣: How to monkey patch a ruby class inside a method

也有大约instance_eval VS class_eval的问题,但我没有一个链接方便。