【问题标题】: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 的问题,但我手头没有链接。

      【讨论】:

        猜你喜欢
        • 2010-09-22
        • 2013-12-26
        • 2012-06-14
        • 2012-03-29
        • 2016-10-30
        • 2020-08-09
        • 1970-01-01
        • 1970-01-01
        • 2021-03-10
        相关资源
        最近更新 更多