【问题标题】:How to know which class raised a NoMethodError?如何知道哪个类引发了 NoMethodError?
【发布时间】:2011-09-19 15:46:27
【问题描述】:

我不想在某个地方引发 NoMethodError,而只是针对某个类(例如 NilClass)。

例如

begin
  maybe_nil_maybe_not_nil = nil
  maybe_nil_maybe_not_nil.x
rescure NoMethodError => ex
  raise unless ex.class_which_raised == NilClass
end

我现在根据异常的 to_s 执行此操作,但是当您不期望 .id、.product 和其他可能的消息时,消息会有所不同。最好基于元数据/参数来执行此操作。有没有办法做到这一点?

【问题讨论】:

    标签: ruby


    【解决方案1】:

    对控制流使用异常通常是个坏主意。如果您调用的方法本身引发了NoMethodError,会发生什么? (它会被你的救援块吞噬)

    你最好使用respond_to?

    raise 'this is bad' if object.nil?
    
    if object.respond_to?(:x)
      object.x
    end
    

    【讨论】:

    • 如果对象没有实现respond_to会发生什么?正确吗?
    • respond_to?为类 Object 声明。除非您使用 BasicObject 类型的对象或来自另一个 ruby​​-meta-programming-stunt 的对象,否则它将按预期工作。此外:这些东西为什么要骗你? ;)
    • 这是一种更简洁的方法。想一想:您通过向它发送一条消息来测试 nil,查看接收方是否退回了该消息,然后才尝试查看退回该消息的对象是否为 nil。如果这就是您要寻找的,为什么不首先测试 nil 呢?
    • 只是因为像 ruby​​-meta-programming 和 define_methodrespond_to 这样的 ruby​​ 程序员没有链接,因此无法保证它们按预期工作。有人说(墨菲?)“如果有什么事情会出错,它就会”。但是,您也可以在比 respond_to 的假设下生存?不会说谎。取决于你的“环境”
    • @mb14:你在说什么?如果你做define_method(:foo) {puts "Brilliant name, genius"},那么obj.respond_to? :foo 将是真的。
    【解决方案2】:

    好吧,根据你在做什么以及你对重新开课的感觉,另一个选择是在你的课程上覆盖 method_missing,例如:

    class NilClass
      alias method_missing old_method_missing
      def method_missing(m_sym, *args, &block)
        old_method_missing m_sym, *args, &block
        log_that_you_had_a_missing_method_call
      end
    end
    

    这有点脏,但有时你无法控制方法的所有调用者,如果你正在处理例如图书馆。我会确保添加很多关于你为什么要猴子修补你的课程的 cmets,因为这里潜在的意外副作用是巨大的。

    我会建议,除非有任何 TOCTOU 问题,否则,如果可能的话,请使用 Matt von Rohr 的“先看再跳”的解决方案。

    【讨论】:

    • 这是我的问题的解决方案。但马特·冯·罗尔的回答听起来更安全 :)
    猜你喜欢
    • 1970-01-01
    • 2021-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多