我所能找到的每个问题的答案(Q1, Q2),关于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
什么是2. 3之前最简洁的等价物?obj&.foo
的方法来避免这些问题?
在Ruby 2.3中,安全导航操作符的工作原理几乎与Ruby 2.3中的 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中的有效语法,这在以前的语言版本中不能只用一个方法调用来模拟。需要注意的是,当你在生产中真正实现这段代码时,你应该看一看 完善 而不是给核心类打补丁。
我认为安全遍历操作符模仿的最类似的方法是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