猴子修补ruby中的类的推荐方法

我注意到有两种常见的方法来修补ruby中的类:

在类上定义新成员,如下所示:

class Array def new_method #do stuff end end 

并在类对象上调用class_eval:

 Array.class_eval do def new_method #do stuff end end 

我想知道两者之间是否存在任何差异以及使用一种方法相对于另一种方法是否有优势?

老实说,我曾经使用过第一种forms(重新开课),因为感觉更自然,但是你的问题迫使我对这个主题做了一些研究,这就是结果。

重新打开该类的问题在于,如果由于某种原因您打算重新打开的原始类目前尚未定义,它将默默地定义一个新类。 结果可能会有所不同:

  1. 如果你没有覆盖任何方法但只添加新的方法并且定义了原始实现(例如,文件,最初定义了类的位置),以后一切都会好的。

  2. 如果重新定义某些方法并且稍后加载原始方法,则将使用其原始版本覆盖您的方法。

  3. 最有趣的情况是当您使用标准自动加载或某些花哨的重新加载机制(如Rails中使用的那种)来加载/重新加载类。 其中一些解决方案依赖于引用未定义常量时调用的const_missing 。 在这种情况下,自动加载机制试图找到未定义的类的定义并加载它。 但是,如果您自己定义类(当您打算重新打开已定义的类)时,它将不再“丢失”,原始可能永远不会被加载,因为不会触发自动加载机制。

另一方面,如果您使用class_eval ,如果class_eval您将立即收到通知。 此外,当您在调用其class_eval方法时引用该类时,任何自动加载机制都将有机会找到类的定义并加载它。

考虑到这一点, class_eval似乎是一种更好的方法。 虽然,我很乐意听到其他一些意见。

范围

我认为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"