【问题标题】:monkey patching vs class_eval?猴子修补 vs class_eval?
【发布时间】:2012-03-13 01:12:14
【问题描述】:
class String
def hello
"world"
end
end
String.class_eval {
def world
"hello"
end
}
"a".world
=> "hello"
"b".hello
=> "world"
他们似乎在做同样的事情——向现有的类添加一个方法。那么有什么区别呢?
【问题讨论】:
标签:
ruby
metaprogramming
monkeypatching
【解决方案1】:
使用class_eval,您可以做更多动态的事情:
>> met = "hello" #=> "hello"
>> String.class_eval "def #{met} ; 'hello' ; end" #=> nil
>> "foo".hello #=> "hello"
【解决方案2】:
class_eval 在概念上进行类重新打开(或猴子修补)。主要存在语法差异。如果您将字符串传递给class_eval(如Michael 的示例中),则字符串中的语法与class String; ... end 中的语法基本相同。如果你通过块:String.class_eval { ... },它的比较如下:
- class_eval 块内部外部局部变量可见
- 内部重新打开的类外部局部变量不可见
- 在 class_eval 内,您不能将常量和类变量分配给该类
- 在重新开放的课程中,您可以
了解其他差异会很有趣
【解决方案3】:
其他答案都很好。想补充一点,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 在某些使用情况下的行为会略有不同。
我觉得这个问题和答案很有趣:
How to monkey patch a ruby class inside a method
还有关于instance_eval 与class_eval 的问题,但我手头没有链接。