问题:
Ruby安全导航操作符(&.
)是否在接收器为nil
时评估其参数?
举例来说:
logger&.log("Something important happened...")
字符串
"Something important happened..."
字符串是否在这里求值?- 你能提供一个权威的来源,证明或否认这一事实?
- 或者建议一种方法来检查它?
先谢了。
为什么我在寻找这个问题的答案?
我在整个代码库中有如下代码:
logger.log("Something important happened. (#{Time.current})") if verbose
型
我的主要目标是在每次调用log
方法时消除if verbose
检查的重复,因为很容易忘记它,并且您根本不会收到有关误用的通知。
受Tell, don't ask原理的启发,
我已经将if verbose
检查移到了log
方法实现中。
class Logger
# ...
def log(message)
return unless verbose
# ...
end
end
def logger
@logger ||= Logger.new
end
logger.log("Something important happened. (#{Time.current})")
型
这种方法简化了我的代码,因为我已经解决了我的主要问题-我不需要记住在调用log
方法时放置if verbose
,
但我收到了另一份"Something important..."
字符串总是被计算,不管verbose
是true
还是false
。
因此,我完全改变了解决方案:
- 当
verbose
是false
时,logger
返回nil
。 - Ruby安全导航操作符应该用在
log
调用前面。
def logger
@logger ||= Logger.new if verbose
end
logger&.log("Something important happened. (#{Time.current})")
型
因此,我将最初的记住if verbose
检查的问题替换为记住&.
调用。
但是,无论如何,我认为这是一个改进,因为忘记使用安全导航操作符会引发NoMethodError
,换句话说,会通知log
方法的误用。
所以现在,为了确保“安全导航操作员方法”实际上是解决我的问题的“更好”选择,
我需要确切地知道Ruby中的安全导航操作符是否在其接收器为nil
时评估其参数。
5条答案
按热度按时间avwztpqn1#
引用安全导航操作符的语法文档:
&.
,称为“安全导航操作符”,允许在接收方为nil
时跳过方法调用。如果跳过调用,则返回nil
,并且不评估方法的参数。因此,当您将
log
方法调用为nil
时,如果logger
为nil
,则不会计算该方法的参数字符串
话虽如此,请注意,Ruby核心日志记录器为您的确切问题提供了不同的解决方案,即避免在日志级别太高时评估潜在的昂贵参数。
Ruby核心记录器实现了类似于以下的
add
方法(简化):型
然后,您可以将其用作
型
在这里,只有当日志级别足够高以至于消息被实际使用时,才对块进行评估。
ijnw1ujt2#
解析表达式但未执行
当
logger.is_a?(NilClass) == true
时,logger&.log
的参数不会被计算。每个被计算的Ruby表达式都应该有影响,所以考虑:字符串
如果参数由解释器计算,test 将等于2。因此,虽然解析器肯定会 * 解析 * 参数中的表达式,但它不会 * 执行 * 内部表达式。
您可以使用Ripper#sexp验证解析器看到的内容:
的数据
这清楚地表明,解析器在符号表达式树中看到了递增的赋值,但是,赋值从来没有真正执行过。
6ojccjat3#
它不评估它们:
字符串
这将返回:
型
如果它计算了它,那么它将触发绑定,就像这个例子一样:
型
如果你没有pry,但有一个现代版本的Ruby,你可以用
binding.irb
代替binding.pry
。这是否是一个“更好”的解决方案是您应该确定的基准。
您可以在How is the Ruby safe navigation (&.) implemented?上阅读更多关于安全导航操作员的信息
azpvetkf4#
不,这很容易测试:
字符串
从概念上讲,你可能会认为安全导航操作符是这样的:
型
现在很清楚为什么它在没有被调用的时候没有被求值。
7y4bm7vi5#
这不是对具体问题的回答,因为这个问题已经得到了回答,而是根据所述目标提出的一种替代办法。
Ruby的Logger已经有了一个块语法,其中块只在 * 当 * 日志级别将打印语句时才被计算。
字符串
如果日志级别为debug或更高,则将对该块进行评估,并记录它返回的字符串。如果日志级别低于debug,则根本不会对该语句进行评估。
TL;DR:将昂贵的日志语句传递给块内的日志记录器。