为什么安全导航比在Rails中使用try更好?

时间:2016-06-22 20:30:16

标签: ruby ruby-on-rails-4

我正在阅读this。使用它的好处是什么:

user&.address&.state

user.try(:address).try(:state)

我仍然没有得到它。

3 个答案:

答案 0 :(得分:56)

(1)&.通常比try(...)

根据具体情况,这可以使您的代码更具可读性。

(2)&.是标准的Ruby,而不是try

方法try未在Ruby核心库中定义,而是在Rails库中定义。当您没有开发RoR Web应用程序但是正在编写例如小助手脚本,这将相当快速。 (例如,我更喜欢Ruby而不是Bash。)

(3)&.使调试更容易

如果调用不存在的方法,则安全遍历运算符将抛出错误。

>> "s"&.glubsch
NoMethodError: undefined method `glubsch' for "s":String

仅在nil上,它会表现得很宽松:

>> nil&.glubsch
=> nil

try方法将始终返回nil

>> "s".try(:glubsch)
=> nil

请注意,最新版本的Ruby和Rails就是这种情况。

现在假设存在名为glubsch的方法的场景。然后你决定重命名该方法但忘记在一个地方重命名它。 (遗憾的是,红宝石会发生这种情况......)使用安全遍历操作符,您几乎会立即注意到错误(只要第一次执行该行代码)。然而,try方法很乐意为您提供nil,并且您将在程序执行的下游某处获得nil相关错误。找出这样的nil来自哪里可能很难。

使用&.快速而艰难地进行调试比使用nil轻快返回try更容易调试。

编辑: 在这方面,还有try!(有一个爆炸)的变体与&.的行为相同。如果您不喜欢&.,请使用它。

(4)如果我不关心方法是否实现怎么办?

那会很奇怪。由于这将是一种意想不到的编程方式,请明确说明。例如,通过使用respond_to?充实两个案例(已实现或未实现)并将其分支。

(5)try的阻止形式怎么样?

可以将块传递给try,而不是方法名称。该块将在接收器的上下文中执行;并且在该区块内没有适用宽大处理。因此,只需一次方法调用,它的行为与&.相同。

>> "s".try{ glubsch }
NameError: undefined local variable or method `glubsch' for "s":String

对于更复杂的计算,您可能更喜欢这种块形式而不是引入大量局部变量。例如。

的链条
foo.try{ glubsch.blam }.try{ bar }.to_s

允许foonil,但需要foo.glubsch才能返回非nil值。然后,您可以使用安全遍历运算符以更简洁的方式执行相同的操作:

foo&.glubsch.blam&.bar.to_s

使用try的块形式进行复杂的计算恕我直言,因为它会妨碍可读性。当您需要实现复杂逻辑时,引入具有描述性名称的局部变量,并使用if来分支nil个案例。您的代码将更易于维护。

HTH!

答案 1 :(得分:20)

(6)速度

安全导航几乎比使用activesupport

中的try方法快4倍
require 'active_support/all'
require 'benchmark'

foo = nil

puts Benchmark.measure { 10_000_000.times { foo.try(:lala) } }
puts Benchmark.measure { 10_000_000.times { foo&.lala } }

输出

  1.310000   0.000000   1.310000 (  1.311835)
  0.360000   0.000000   0.360000 (  0.353127)

答案 2 :(得分:2)

我不认为我们应该比较这两件事,因为它们还有其他作用。

给出此示例Person

class Person
  def name
    "John"
  end
end

尝试

如果不确定不确定对象上是否存在给定方法 ,则应使用

try

person = Person.new
person.name        # => "John"
person.email       # => NoMethodError: undefined method `email' for #<Person>
person.try(:email) # => nil

&。

如果不确定在 on 上调用方法的对象是否为空,则应使用安全导航

person = nil
person.name  # => NoMethodError: undefined method `name' for nil:NilClass
person&.name # => nil

您不能互换使用

人们经常混淆这两种方法,因为您可以使用try而不是&.

person = nil
person.name       # => NoMethodError: undefined method `name' for nil:NilClass
person&.name      # => nil
person.try(:name) # => nil

但是您不能使用&.代替try

person = Person.new
person.name        # => "John"
person.email       # => NoMethodError: undefined method `email' for #<Person>
person.try(:email) # => nil
person&.email      # NoMethodError: undefined method `email' for #<Person>