预Ruby2.3与安全导航操作员(“或”符号与点“)的等效值是多少?

预Ruby2.3与安全导航操作员(“或”符号与点“)的等效值是多少?,ruby,null,operators,ruby-2.3,safe-navigation-operator,Ruby,Null,Operators,Ruby 2.3,Safe Navigation Operator,关于Ruby新的安全导航操作符(&.)我能找到的每个问题(,)的答案都错误地声明obj&.foo等同于obj&&obj.foo 很容易证明这种等价性是不正确的: obj = false obj && obj.foo # => false obj&.foo # => NoMethodError: undefined method `foo' for false:FalseClass 此外,还有多重评估的问题。用具有副作用的表达式替换obj,表明

关于Ruby新的安全导航操作符(
&.
)我能找到的每个问题(,)的答案都错误地声明
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
相比,2.3版之前最简洁的方法是什么,可以避免这些问题?

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

如果方法无法计算给定的方法,则返回nil

我们可以通过以下方式重写
try
pretty:

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

Ruby 2.3中的安全导航操作符的工作原理几乎与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
    是否实际实现了查询的方法
  • 如果非
    nil
    接收器不支持该方法,则会引发
    NoMethodError
  • 它不会接受方法调用上的任何异常
由于语言语义的差异,它无法(完全)实现real safe navigation operator的其他功能,包括:

  • 与安全导航操作符相比,我们的
    try
    方法总是计算其他参数。考虑这个例子

    nil&.foo(bar())
    
    这里,
    bar()
    不计算。当使用我们的
    方法时,请尝试

    nil.try(:foo, bar())
    
    我们总是先调用
    bar
    方法,不管以后是否使用它调用
    foo

  • obj&.attr+=1
    是Ruby 2.3.0中的有效语法,在以前的语言版本中,仅用一个方法调用无法模拟该语法

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

我认为没有2.3之前的版本。这就是他们添加它的原因。但是考虑到Rails
try
方法的功能似乎与
&
类似,我猜它们的实现非常类似:
inc.try(:自身)#=>1
。你可以在这里查看
try
源代码@Mike我能想到
(x=inc;x.self,除非x.nil?)
作为2.3版本之前的等效版本。我希望有一些不那么冗长的东西。ActiveSupport的
try
可能会复制大量的
&.
功能,但它并不是直接的等价物。
&.
并不等同于
obj&&obj.foo
,但在大多数情况下是这样的。u&u.profile作为u&u.profile的缩写形式提醒我们。不可否认,这是吹毛求疵的高度,但我不确定这是否完全等同。给定
obj=nil
,我得到
obj&.foo&.nil?#=>无
obj.try(:foo)。try(:nil?)35;=>真
。有趣的观察。我承认我被难住了。在这种情况下,中介“
nil
但并非真正的
nil
”值通过安全遍历操作符返回,而上述
try
的定义不起作用。(1)您在
public\u send
上接受异常;(2)在
nil
本身的任何方法调用上都不返回
nil
,这两种情况都与安全导航操作员的行为不同。1)您不接受异常。
send
public\u send
之间的区别在于
public\u send
只调用public方法。如果该方法不存在,它仍然会引发一个
NoMethodError
,如果该方法确实存在并且存在错误,那么代码也会出现错误。2)
nil
点是有效的,但如上所述,它需要一个不同于
nil
的返回类型,才能像安全导航操作符一样工作。@yes
self.public\u send(method)rescue nil
接受任何可能引发的异常,包括
NoMethodError
,无论您是在此处使用
send
还是
public\u send
。而且,你不需要“另一个”零。如果接收器为
nil
,则安全导航操作员始终返回
nil
。与Ruby中其他地方使用的nil相同。除了它的块处理,你是什么意思?好吧,你可以调用ActiveSupport的
try
方法带有一个块(没有其他参数),它将屈服于该块,除非接收方是
nil
。这是不可能的安全导航操作员。有关详细信息,请参阅(上面的链接)。正如Jesse Sielaff在上面的评论中指出的那样,您的代码不能处理
try(:nil?
on
nil
)<代码>a=无;a、 试试(:nil?#=>nil
,这正是安全导航操作员所做的<在Ruby 2.3.0中,code>nil和.nil?返回
nil
。我对句子进行了更精确的编辑。基本上,您需要做的是检查接收器,然后调用
attr
attr=
,如果它不是
nil
。由于这两个方法调用彼此独立(即接收方
obj)
无法知道它是用
+=
运算符调用的,因此无法在单个
try
调用中处理它。