我需要克隆一个现有对象并更改该克隆对象。 问题是我的更改更改了原始对象。 这是代码:
require "httparty"
class Http
attr_accessor :options
attr_accessor :rescue_response
include HTTParty
def initialize(options)
options[:path] = '/' if options[:path].nil? == true
options[:verify] = false
self.options = options
self.rescue_response = {
:code => 500
}
end
def get
self.class.get(self.options[:path], self.options)
end
def post
self.class.post(self.options[:path], self.options)
end
def put
self.class.put(self.options[:path], self.options)
end
def delete
self.class.put(self.options[:path], self.options)
end
end
场景:
test = Http.new({})
test2 = test
test2.options[:path] = "www"
p test2
p test
输出:
#<Http:0x00007fbc958c5bc8 @options={:path=>"www", :verify=>false}, @rescue_response={:code=>500}>
#<Http:0x00007fbc958c5bc8 @options={:path=>"www", :verify=>false}, @rescue_response={:code=>500}>
有没有办法解决这个问题?
答案 0 :(得分:2)
您想要.clone
还是.dup
test2 = test.clone
但是根据您的目的,但是在这种情况下,您可能想要.clone
参见What's the difference between Ruby's dup and clone methods?
主要区别在于.clone
还会复制对象单例方法和冻结状态。
在旁注中,您也可以更改
options[:path] = '/' if options[:path].nil? # you don't need "== true"
答案 1 :(得分:2)
您甚至不需要在这里克隆,只需要创建一个新实例即可。
就在这里:
test = Http.new({})
test2 = test
您没有Http的两个实例,您只有一个。您只有两个变量指向同一个实例。
您可以改为将其更改为此,这样就不会有问题。
test = Http.new({})
test2 = Http.new({})
但是,如果您使用共享的options
参数,那就是遇到问题的地方:
options = { path: nil }
test = Http.new(options)
# options has been mutated, which may be undesirable
puts options[:path] # => "/"
为避免这种“副作用”,您可以将initialize方法更改为使用选项的克隆:
def initialize(options)
options = options.clone
# ... do other stuff
end
您还可以使用splat运算符,它有点神秘,但可能更惯用:
def initialize(**options)
# do stuff with options, no need to clone
end
然后您将像这样调用构造函数:
options = { path: nil }
test = Http.new(**options)
puts test.options[:path] # => "/"
# the original hasn't been mutated
puts options[:path] # => nil