预Ruby2.3与安全导航操作员(“或”符号与点“)的等效值是多少?
关于Ruby新的安全导航操作符(预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,表明
&.
)我能找到的每个问题(,)的答案都错误地声明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
- 它不会接受方法调用上的任何异常
- 与安全导航操作符相比,我们的
方法总是计算其他参数。考虑这个例子try
这里,nil&.foo(bar())
不计算。当使用我们的bar()
方法时,请尝试
我们总是先调用nil.try(:foo, bar())
方法,不管以后是否使用它调用bar
foo
是Ruby 2.3.0中的有效语法,在以前的语言版本中,仅用一个方法调用无法模拟该语法obj&.attr+=1
请注意,在实际生产中实现此代码时,您应该查看一下,而不是修补核心类。我认为没有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
的返回类型,才能像安全导航操作符一样工作。@yesself.public\u send(method)rescue nil
接受任何可能引发的异常,包括NoMethodError
,无论您是在此处使用send
还是public\u send
。而且,你不需要“另一个”零。如果接收器为nil
,则安全导航操作员始终返回nil
。与Ruby中其他地方使用的nil相同。除了它的块处理,你是什么意思?好吧,你可以调用ActiveSupport的try
方法带有一个块(没有其他参数),它将屈服于该块,除非接收方是nil
。这是不可能的安全导航操作员。有关详细信息,请参阅(上面的链接)。正如Jesse Sielaff在上面的评论中指出的那样,您的代码不能处理try(:nil?
onnil
)<代码>a=无;a、 试试(:nil?#=>nil,这正是安全导航操作员所做的<在Ruby 2.3.0中,code>nil和.nil?返回nil
。我对句子进行了更精确的编辑。基本上,您需要做的是检查接收器,然后调用attr
和attr=
,如果它不是nil
。由于这两个方法调用彼此独立(即接收方obj)
无法知道它是用+=
运算符调用的,因此无法在单个try
调用中处理它。