【问题标题】:Why does Object::try work if it's sent to a nil object?如果将 Object::try 发送到 nil 对象,为什么它会起作用?
【发布时间】:2013-10-07 12:15:08
【问题描述】:

如果您尝试在 Ruby 中调用 nil 对象上的方法,则会出现 NoMethodError 异常并显示以下消息:

"undefined method ‘...’ for nil:NilClass"

但是,Rails 中有一个 try 方法,如果它被发送到 nil 对象,它只会返回 nil

require 'rubygems'
require 'active_support/all'

nil.try(:nonexisting_method) # no NoMethodError exception anymore

那么try 如何在内部工作以防止出现该异常?

【问题讨论】:

  • "如果您尝试调用 nil 对象上的方法...出现异常" => 错误。 “如果您尝试在 nil 上调用 undefined for NilClass 方法...出现异常” => 正确。

标签: ruby-on-rails ruby activesupport


【解决方案1】:

像 Ruby 中的所有其他对象一样,nil 有一个类 (NilClass),它有方法。例如,nil?NilClass 上定义为返回 true,这就是 nil.nil? 不返回 NoMethodError 的原因。

ruby 中的每个类都可以打开(使用新方法更新)。 Rails 打开NilClass 以添加try 方法并让它返回nil(这也是为什么即使链中的一个步骤返回nil 也可以链接try,因为随后的nil 回复try)。

【讨论】:

    【解决方案2】:

    ActiveSupport 4.0.0 定义了两个try 方法:one 用于Object 实例:

    class Object
      def try(*a, &b)
        if a.empty? && block_given?
          yield self
        else
          public_send(*a, &b) if respond_to?(a.first)
        end
      end
    end
    

    other 用于 NilClass 实例(nil 对象):

    class NilClass
      def try(*args)
        nil
      end
    end
    

    现在,假设我们有一个Object 实例(不包括nil,它实际上继承自Object,就像Ruby 中的所有其他东西一样),定义一个返回nil 的方法:

    class Test
      def returns_nil
        nil
      end
    end
    

    因此,运行Test.new.try(:returns_nil)Test.new.not_existing_method,将调用Object#try,这将检查是否存在公共方法(respond_to?);如果这样做,它将调用该方法(public_send),否则它将返回nil(没有其他行)。

    如果我们在这些返回的nil 方法中调用另一个try

    Test.new.try(:returns_nil).try(:any_other_method)
    Test.new.try(:not_existing_method).try(:any_other_method)
    

    我们将调用NilClass#try,即nil#try,它会简单地忽略所有内容并返回nil。因此,任何其他 try 都将在 nil 实例上调用并返回 nil

    【讨论】:

      【解决方案3】:

      来自 Rails (3.2) documentation:

        # Object#try:
        def try(*a, &b)
          if a.empty? && block_given?
            yield self
          else
            __send__(*a, &b)
          end
        end
      
        # NilClass#try (http://apidock.com/rails/NilClass/try):
        def try(*args)
          nil
        end
      

      方法try 只是为nil 对象单独实现,因此它总是返回nil,即使您尝试调用的方法是为nil 对象实现的。

      【讨论】:

        【解决方案4】:

        它忽略参数并只返回nil。这是implementation

        class NilClass
          def try(*args)
            nil
          end
        end
        

        注意这个实现总是返回nil,即使nil响应了方法:

        nil.try(:to_i) # => nil, rather than 0
        

        【讨论】:

          猜你喜欢
          • 2014-12-14
          • 2011-10-04
          • 1970-01-01
          • 2018-02-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-11-16
          • 2013-03-16
          相关资源
          最近更新 更多