前几天我注意到Ruby中实例变量的一些奇怪行为。我试图添加一个实例变量数组,包含类的其他实例变量"属性"。类初始化时没有任何参数,但我仍然希望在初始化时创建这个数组。下面是一个(精简)类的例子:
class Foo
attr_accessor :bar, :baz
attr_reader :attrs
def initialize
@attrs = [@bar, @baz]
end
end
奇怪的是:
f = Foo.new #=><Foo.0x[object_id] @attrs=[nil, nil]>
f.bar = "bar" #=>"bar"
f.baz = "baz" #=>"baz"
f.attrs #=>[nil, nil]
初始化时,我可以看到Foo.attrs
是[nil, nil]
。但是在更新了Foo.bar
和Foo.baz
之后,为什么Foo.attrs
仍然返回[nil, nil]
?为什么他们的新价值观没有得到体现?
我认为这不是最好的方法,并找到了一种绕过它的方法,但我仍然对这种行为感到好奇。
因为这就是变量的工作方式,在这里和几乎所有其他编程语言中都是如此。
你的数组包含@bar
和@baz
在创建数组时的值。它不包含对变量本身的引用。修改一个不修改另一个。
你已经有效地做到了:
x = 3;
y = x;
x = 4;
# Why doesn't y equal 4?
y
不是4
,因为x
和y
共享值,但在其他方面不相关。对x
重新赋值不会改变y
的值
如果你想让它工作,你需要创建一个按需构建数组的访问器,使用成员变量的当前值:
class Foo
attr_accessor :bar, :baz
def attrs
[@bar, @baz]
end
end
您可以简单地添加一个puts
,看看会发生什么
class Foo
attr_accessor :bar, :baz
attr_reader :attrs
def initialize
@attrs = [@bar, @baz]
puts "inside initialize"
end
end
现在你可以看到当你创建Foo
f = Foo.new
#=> inside initialize
#=> #<Foo:0x2bc1bb0 @attrs=[nil, nil]>
f.bar = "bar" #=>"bar" , "inside initialize" not printed
如果你想让它们被赋值,那么创建一个setter
class Foo
attr_accessor :bar, :baz
attr_reader :attrs
def initialize
@attrs = [@bar, @baz]
puts "inside initialize"
end
def bar=(v)
@bar = v
@attrs = [@bar,@baz]
end
def baz=(v)
@baz = v
@attrs = [@bar,@baz]
end
end
f = Foo.new
#=> inside initialize
#=> #<Foo:0x2bc1bb0 @attrs=[nil, nil]>
f.bar = "bar"
f.attrs #=> ["bar", nil]
f.baz = "baz"
f.attrs #=> ["bar", "baz"]