我可以找到的关于Ruby的新安全导航操作符(&.
)的每个问题(Q1,Q2)的答案错误地声明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
以避免这些问题?
答案 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