我注意到有两种常见的方法来修补ruby中的类:
在类上定义新成员,如下所示:
class Array
def new_method
#do stuff
end
end
在类对象上调用class_eval:
Array.class_eval do
def new_method
#do stuff
end
end
我想知道两者之间是否存在差异,使用一种方法是否有优势?
答案 0 :(得分:56)
老实说,我曾经使用过第一种形式(重新开课),因为感觉更自然,但是你的问题迫使我对这个主题做了一些研究,这就是结果。
重新打开类的问题在于,如果由于某种原因您打算重新打开的原始类目前尚未定义,它将默默地定义一个新类。结果可能会有所不同:
如果你没有覆盖任何方法但只添加新的方法并且定义了原始实现(例如,文件,最初定义了类的位置),以后一切都会好的。
如果您重新定义某些方法并且稍后加载原始文件,则会使用原始版本覆盖您的方法。
最有趣的情况是当您使用标准autoloading或某些花哨的重新加载机制(如Rails中使用的机制)来加载/重新加载类时。其中一些解决方案依赖于引用未定义常量时调用的const_missing。在这种情况下,自动加载机制会尝试查找未定义的类的定义并加载它。但是,如果您自己定义类(当您打算重新打开已定义的类)时,它将不再“丢失”,原始可能永远不会被加载,因为不会触发自动加载机制。 / p>
另一方面,如果你使用class_eval
,如果目前没有定义课程,你会立即收到通知。此外,当您在调用其class_eval
方法时引用该类时,任何自动加载机制都有机会找到类的定义并加载它。
考虑到这一点class_eval
似乎是一种更好的方法。虽然,我很乐意听到其他一些意见。
答案 1 :(得分:7)
<强>范围强>
我认为,KL-7没有指出的一个重大区别是您的新代码将被解释的范围:
如果您(重新)打开一个类来操作它,您添加的新代码将在(原始)类的范围内进行解释。
如果使用Module#class_eval来操作类,则添加的新代码将在围绕调用#class_eval的范围内进行解释,并且不会知道类范围。如果一个人不知道,这种行为可能会违反直觉并导致难以调试的错误。
CONSTANT = 'surrounding scope'
# original class definition (uses class scope)
class C
CONSTANT = 'class scope'
def fun() p CONSTANT end
end
C.new.fun # prints: "class scope"
# monkey-patching with #class_eval: uses surrounding scope!
C.class_eval do
def fun() p CONSTANT end
end
C.new.fun # prints: "surrounding scope"
# monkey-patching by re-opening the class: uses scope of class C
class C
def fun() p CONSTANT end
end
C.new.fun # prints: "class scope"