我希望你们能帮助我决定哪种行为更"标准"或"预期"。
背景:
在 Ruby 中,您可以为方法提供默认参数,例如。
def tester(input={})
input
end
如果你打电话给tester
你会得到{}
.但是,如果您打电话给tester(nil)
,则会nil
。
老实说,这让我感到惊讶,我认为如果您将nil
传递给该方法,您会得到{}
,因为我总是认为方法定义中input={}
语句类似于input ||= {}
,但显然它更像是defined?(input) ? input : {}
。
问题:
在我维护的一个库中,我有一个类,它在初始化时采用可选的哈希。初始值设定项看起来就像上面的测试器一样:
def initialize(input={})
所以你可以打电话给MyClass.new
或MyClass.new foo:1
,但你不能打电话给MyClass.new nil
这似乎很明显,除非使用变量进行初始化,例如。 MyClass.new(opts)
opts 可以是哈希或 nil。
我可以通过更改实现来更改其工作方式,如下所示:
def initialize(input=nil)
input ||= {}
...
end
但是,我想知道,这是Ruby界面的正确设计吗?
我应该期望调用者使用MyClass.new(opts || {})
吗?
如果您使用的是库,并且该库中有一个接受可选哈希的类,您是否希望将nil
传递给初始值设定项并将其视为等效于不传递参数是安全的?或者你会期望它中断,因为在 Ruby 中,传递字面上的 nil 与传递任何参数不同?
哪种设计对您来说更"正确"?
默认值仅适用于未指定的参数,不适用于提供的参数,而是nil
。在Ruby中,这是一个值,甚至是一个对象,它不像其他语言那样带有"undefined"或"null"。
你对使用||= { }
的观察是许多库如何处理这种事情。我通常设计代码在面对意外传递nil
时具有弹性,因为这可能会错误地发生,并且似乎是对如何做我的意思而又不过分偏执和防御的合理解释。
通常,这只是= { }
或= [ ]
类型情况下的问题。从某种意义上说,这是Ruby原则,即传入足够接近工作的东西,或者至少不是不正确的。想想如何使用该方法并适应所有合理情况。
因此,您倾向于看到的签名如下所示:
def with_options(args = nil)
# Default arguments to an empty Hash
args ||= { }
# ...
end
def trim(string, length = 1)
string = string.to_s # Coerce to string
length = length.to_i # Coerce to integer
# ...
end
对于内部使用的代码,通常最好更严格,如果方法使用不正确,则抛出错误,即防御性较低,但对于可能不能使所有内容完全正确的各方使用的方法,始终赞赏一定程度的自由度。
在像 Rails 这样的环境中,传递"1"
而不是1
进行数值论证太容易了,惩罚某人没有正确投射可能太残忍了。至少尝试转换并处理结果。