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

Jwa*_*622 31 ruby ruby-on-rails-4

我正在读这个.使用它有什么好处:

user&.address&.state
Run Code Online (Sandbox Code Playgroud)

过度

user.try(:address).try(:state)
Run Code Online (Sandbox Code Playgroud)

我还是不明白.

Raf*_*ael 66

(1)&.一般比短于try(...)

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

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

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

(3)&.使调试更容易

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

>> "s"&.glubsch
NoMethodError: undefined method `glubsch' for "s":String
Run Code Online (Sandbox Code Playgroud)

只有nil它会表现得很宽松:

>> nil&.glubsch
=> nil
Run Code Online (Sandbox Code Playgroud)

try方法将始终返回nil.

>> "s".try(:glubsch)
=> nil
Run Code Online (Sandbox Code Playgroud)

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

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

如果不快速,很难与&.品牌调试比轻率地返回更容易niltry.

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

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

那会很奇怪.由于这将是一种意想不到的编程方式,请明确说明.例如,通过使用respond_to?和分支两个案例(已实现与否)来充实它们.

(5)try封锁形式怎么样?

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

>> "s".try{ glubsch }
NameError: undefined local variable or method `glubsch' for "s":String
Run Code Online (Sandbox Code Playgroud)

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

foo.try{ glubsch.blam }.try{ bar }.to_s
Run Code Online (Sandbox Code Playgroud)

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

foo&.glubsch.blam&.bar.to_s
Run Code Online (Sandbox Code Playgroud)

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

HTH!

  • 这是我当天的非建设性评论:感谢您不使用`foo`作为默认变量.`glubsch`更有趣. (16认同)
  • 响应很旧,但只是一个评论,意思是在最后一点,`try` 版本和 `&.` 版本的行为不同。foo 为 `nil` 时的示例:try version 将导致 `nil.to_s` 而 `&.` 版本将尝试在 `nil` 上调用 `blam` 并引发 NoMethodError (2认同)

ror*_*rra 23

(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 } }
Run Code Online (Sandbox Code Playgroud)

产量

  1.310000   0.000000   1.310000 (  1.311835)
  0.360000   0.000000   0.360000 (  0.353127)
Run Code Online (Sandbox Code Playgroud)

  • 它在我的机器上似乎快了3倍,但是当`foo`实际上不是零时,你忘了做案.所以我写了以下基准:https://gist.github.com/alchimere/1cd424ee8b093867a88d82026734c78c,对于非零对象,`&.`似乎比'try`快**8倍** (3认同)

har*_*mag 6

我认为我们不应该比较这两件事,因为它们做了别的事情。

鉴于这个示例Person

class Person
  def name
    "John"
  end
end
Run Code Online (Sandbox Code Playgroud)

尝试

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

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

&.

如果你不知道,如果你调用你的方法的对象安全nagivation应使用零或不

person = nil
person.name  # => NoMethodError: undefined method `name' for nil:NilClass
person&.name # => nil
Run Code Online (Sandbox Code Playgroud)

你不能互换使用它们

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

person = nil
person.name       # => NoMethodError: undefined method `name' for nil:NilClass
person&.name      # => nil
person.try(:name) # => nil
Run Code Online (Sandbox Code Playgroud)

但是你不能用&.代替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>
Run Code Online (Sandbox Code Playgroud)