San*_*osh 181

它被称为安全导航操作员.在Ruby 2.3.0中引入,它允许您调用对象上的方法而不必担心对象可能nil(避免undefined method for nil:NilClass错误),类似于tryRails中方法.

所以你可以写

@person&.spouse&.name
Run Code Online (Sandbox Code Playgroud)

代替

@person.spouse.name if @person && @person.spouse
Run Code Online (Sandbox Code Playgroud)

来自Docs:

my_object.my_method

这会将my_method消息发送到my_object.任何对象都可以是接收者,但根据方法的可见性,发送消息可能会引发NoMethodError.

你可以使用&.指定一个接收器,然后调用my_method,当接收器为零时结果为nil.在这种情况下,不评估my_method的参数.

  • 实际上,它的工作方式类似于`try!`方法,而不是`try` (12认同)

Uzb*_*jon 54

注意:即使@Santosh给出了一个清晰而完整的答案,我想添加一些更多的背景,并添加一个关于它与非实例变量一起使用的重要说明.


它被称为" 安全导航操作员 "(又名"可选链接操作符","空条件操作符"等).Matz似乎称之为"孤独的运营商".它是在Ruby 2.3引入的.它仅在不对象时才向对象发送方法nil.

例:

# Call method `.profile` on `user` only if `user` is not `nil`
@user&.profile

# Equivalent to
unless @user.nil?
  @user.profile
end
Run Code Online (Sandbox Code Playgroud)

带有局部变量的"边缘情况":

请注意,上面的代码使用实例变量.如果要将安全导航运算符与局部变量一起使用,则必须先检查是否已定义局部变量.

# `user` local variable is not defined previous
user&.profile

# This code would throw the following error:
NameError: undefined local variable or method `user' for main:Object
Run Code Online (Sandbox Code Playgroud)

要解决此问题,请检查是否首先定义了局部变量或将其设置为nil:

# Option 1: Check the variable is defined
if defined?(user)
  user&.profile
end

# Option 2: Define your local variable. Example, set it to nil
user = nil
user&.profile     # Works and does not throw any errors
Run Code Online (Sandbox Code Playgroud)

方法背景

Rails有try基本相同的方法.它在send内部使用方法来调用方法.Matz认为它很慢,这应该是一个内置的语言功能.

许多其他编程语言具有类似的功能:Objective C,Swift,Python,Scala,CoffeeScript等.但是,常见的语法是?.(问题点).但是,Ruby不能采用这种语法.因为?在方法名称中允许,因此?.符号序列已经是有效的Ruby代码.例如:

2.even?.class  # => TrueClass
Run Code Online (Sandbox Code Playgroud)

这就是为什么Ruby社区必须提出不同的语法.这是一个积极的讨论,被认为不同的选项(.?,?,&&等等).以下列出了一些注意事项:

u.?profile.?thumbnails
u\profile\thumbnails
u!profile!thumbnails
u ? .profile ? .thumbnails
u && .profile && .thumbnails

# And finally
u&.profile&.thumbnails
Run Code Online (Sandbox Code Playgroud)

在选择语法时,开发人员会查看不同的边缘情况,讨论非常有用.如果您想了解运营商的所有变体和细微差别,请参阅官方Ruby问题跟踪器上的此功能介绍讨论.

  • 确实,正如我写的`.?`和其他选项被考虑,但是`&.`被选中了!所以,只有`&.`才能奏效. (2认同)
  • @iBug 有人提到过Python吗?我不这么认为。 (2认同)

VKa*_*atz 8

safe navigation operator (&.):告诉 Ruby 仅在接收者\xe2\x80\x99t nil 时调用下一个方法。否则,表达式返回 nil。

\n
\n

实际行动

\n

让\xe2\x80\x99s 构造Roster一个Sports team. 遗嘱Roster包含multiple Player对象。

\n
class Roster\n  attr_accessor :players\nend\n\nclass Player\n  attr_accessor :name, :position\n  \n  def initialize(name, position)\n    @name = name\n    @position = position\n  end\n\nend\n
Run Code Online (Sandbox Code Playgroud)\n

有了这两个对象,我们可以创建 2 对 2 女子\xe2\x80\x99s 篮球锦标赛的名单:

\n
moore = Player.new("Maya Moore", "Forward")\ntaurasi = Player.new("Diana Taurasi", "Guard")\ntourney_roster1 = Roster.new\ntourney_roster1.players = [moore, taurasi]\n
Run Code Online (Sandbox Code Playgroud)\n

如果我们想知道我们球队的前锋2-on-2,我们可以这样找到名字:

\n
if tourney_roster1.players.first.position == "Forward"\n  puts "Forward: #{tourney_roster1.players.first.name}"\nend\n
Run Code Online (Sandbox Code Playgroud)\n
\n

但是如果我们的对手名单\xe2\x80\x99设置不正确怎么办?

\n
\n
tourney_roster2 = Roster.new\nif tourney_roster2.players.first.position == "Forward"\n  puts "Forward: #{tourney_roster1.players.first.name}"\nend\n
Run Code Online (Sandbox Code Playgroud)\n

tourney_roster2尚未对任何播放器设置\xe2\x80\x99。前面的代码将引发一个NoMethodError because tourney_roster2.players returns nil. 我们可以添加条件语句来避免这种情况,但这会使我们的 if 语句变得冗长且不清楚:

\n
if tourney_roster2.players &&\n   tourney_roster2.players.first &&\n   tourney_roster2.players.first.position == "Forward"\n
Run Code Online (Sandbox Code Playgroud)\n

相反,我们可以使用安全导航运算符来避免 NoMethodError:

\n
if tourney_roster2.players&.first&.position == "Forward"\n  puts "Forward: #{tourney_roster1.players.first.name}"\nend\n
Run Code Online (Sandbox Code Playgroud)\n

因此,

\n
 >> tourney_roster2.players&.first == nil\n  #=> true\n >> tourney_roster2.players&.first&.position == nil\n  #=> true\n
Run Code Online (Sandbox Code Playgroud)\n

一些合法的用例:在处理多个对象时(如此处所示)以及将方法链接在一起时,安全导航运算符会派上用场。

\n


Tho*_*nek 7

警惕!尽管安全的导航操作员很方便,但也很容易欺骗自己改变逻辑。我建议避免在流量控制中使用它。例:

str = nil

puts "Hello" if str.nil? || str.empty?
# The above line is different than the below line
puts "Hello" if str&.empty?
Run Code Online (Sandbox Code Playgroud)

在第一个示例中,str.nil?return true并且str.empty?从不被调用,从而导致该puts语句被执行。但是,在第二个示例中,str&.empty?返回nilfalse,并且该puts语句从不执行。

  • 的确,使用错误的逻辑会容易得多,但是您要在此处检查其他内容。第二行等效于str && str.empty?不为零|| 空的。除非str&.present?(除非仅用于present?方法),否则安全导航操作员仍然非常有效地用于流程控制“放置”。 (2认同)