2012-02-22 72 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的問題,但我沒有一個鏈接方便。