什么是Ruby2.3之前的安全导航操作符(`& .`)?

时间:2016-01-04 23:59:16

标签: ruby null operators ruby-2.3 safe-navigation-operator

我可以找到的关于Ruby的新安全导航操作符(&.)的每个问题(Q1Q2)的答案错误地声明obj&.foo等同于{{ 1}}。

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

obj && obj.foo

此外,存在多重评估的问题。用带有副作用的表达式替换obj = false obj && obj.foo # => false obj&.foo # => NoMethodError: undefined method `foo' for false:FalseClass 表示副作用仅在obj表达式中加倍:

&&

什么是最简洁的2.3之前相当于def inc() @x += 1 end @x = 0 inc && inc.itself # => 2 @x = 0 inc&.itself # => 1 以避免这些问题?

2 个答案:

答案 0 :(得分:1)

Ruby 2.3中的安全导航操作符几乎与ActiveSupport添加的try!方法完全相同,减去了它的块处理。

简化版本可能如下所示:

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
  • 它不会吞下方法调用的任何异常。

由于语言语义的差异,它无法(完全)实现真正安全导航操作符的其他功能,包括:

  • 与安全导航操作符相比,我们的try方法始终评估其他参数。考虑这个例子

    nil&.foo(bar())
    

    此处,未评估bar()。使用我们的try方法时

    nil.try(:foo, bar())
    

    我们总是首先调用bar方法,无论我们之后是否使用它来调用foo

  • obj&.attr += 1是Ruby 2.3.0中的有效语法,无法使用以前语言版本中的单个方法调用进行模拟。

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

答案 1 :(得分: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