3

浏览 Rails 代码库,我发现许多对 options.dup 的引用。

def to_xml(options = {})
  require 'builder' unless defined?(Builder)
  options = options.dup
  ....
end

显然 options.dup 是在复制 options 哈希,但你为什么要在这种情况下这样做呢?

4

2 回答 2

7

dup克隆一个对象。当您将对象传递给方法时,任何更改该对象内部状态的内容都将反映在调用范围中。例如,试试这个代码:

def replace_two(options)
  options[:two] = "hi there"
end

options = { one: "foo", two: "bar" }
replace_two(options)
puts options[:two]

那将打印hi there,因为replace_two()修改了哈希内容。

如果您想避免更改传入的options,您可以调用.dup它,然后对克隆所做的任何更改都不会反映在调用范围中:

def replace_two(options)
  options = options.dup
  options[:two] = "hi there"
end

options = { one: "foo", two: "bar" }
replace_two(options)
puts options[:two]

将打印bar。这是遵循最小惊讶原则的常见模式。在 Ruby 中,修改其参数的方法通常以!后缀命名,以提醒用户它们是破坏性/修改操作。dup应该调用该方法的非版本replace_two!来指示这种副作用。

于 2013-03-28T12:36:56.307 回答
5

dup创建对象的浅表副本。这是红宝石核心的东西。由于像 Hash 和 Array 这样的 ruby​​ 对象是通过引用传递的,因此当您在函数内部更改对象时,这将更改原始对象。如果这不是所需的行为 - 您创建一个副本......那么代码就可以了。

红宝石文档

更新

还有一件事。由于对象是通过引用传递的,options = options.dup因此将分配给options新创建的副本的变量引用。对原始对象的引用在内部丢失to_xml。但它仍然可能在调用的代码中被引用to_xml

于 2013-03-28T12:32:21.723 回答