3

我能找到的关于 Ruby 的新安全导航运算符 ( ) 的每个问题的答案(Q1Q2&. )都错误地声明它obj&.foo等同于obj && obj.foo.

很容易证明这种等价是不正确的:

obj = false
obj && obj.foo  # => false
obj&.foo        # => NoMethodError: undefined method `foo' for false:FalseClass

此外,存在多重评估的问题。用具有副作用的表达式替换obj表明副作用仅在&&表达式中加倍:

def inc() @x += 1 end

@x = 0
inc && inc.itself  # => 2

@x = 0
inc&.itself        # => 1

obj&.foo避免这些问题的最简洁的 pre-2.3 等价物是什么?

4

2 回答 2

1

Ruby 2.3 中的安全导航运算符的工作try!方式与 ActiveSupport 添加的方法几乎完全相同,只是减去了块处理。

它的简化版本可能如下所示:

class Object
  def try(method, *args, &block)
    return nil if self.nil?
    public_send(method, *args, &block)
  end
end

你可以像这样使用它

obj.try(:foo).try(:each){|i| puts i}

try方法实现了安全导航算子的各种细节,包括:

  • nil如果接收者是,它总是返回nil,不管是否nil实际实现了查询的方法。
  • NoMethodError如果非nil接收者不支持该方法,它会引发 a 。
  • 它不会吞下方法调用的任何异常。

由于语言语义上的差异,它不能(完全)实现真正安全导航算子的其他特性,包括:

  • try与安全导航运算符相反,我们的方法总是评估附加参数。考虑这个例子

    nil&.foo(bar())
    

    在这里,bar()不予评价。当使用我们的try方法时

    nil.try(:foo, bar())
    

    我们总是bar先调用该方法,无论我们是否稍后调用foo它。

  • obj&.attr += 1是 Ruby 2.3.0 中的有效语法,在以前的语言版本中不能仅通过单个方法调用来模拟。

请注意,在生产中实际实现此代码时,您应该查看Refinements而不是修补核心类。

于 2016-01-05T12:27:33.487 回答
0

我认为安全遍历运算符模拟的最相似的方法是 Rails 的try方法。但不完全是,我们需要处理对象nil不响应但也不响应方法的情况。

如果方法无法评估给定的方法,它将返回 nil。

我们可以try很简单地重写:

class Object
  def try(method)
    if !self.respond_to?(method) && !self.nil?
      raise NoMethodError, "undefined method #{ method } for #{ self.class }"
    else
      begin
        self.public_send(method) 
      rescue NoMethodError
        nil
      end
    end
  end
end

然后它可以以几乎相同的方式使用:

Ruby 2.2 及更低版本:

a = nil
a.try(:foo).try(:bar).try(:baz)
# => nil

a = false
a.try(:foo)
# => NoMethodError: undefined method :foo for FalseClass

Ruby 2.3 中的等价物

a = nil
a&.foo&.bar&.baz
# => nil

a = false
a&.foo
# => NoMethodError
于 2016-01-05T00:22:12.233 回答