我遇到了一些奇怪的行为,想知道是否有人能证实我所看到的。
假设您创建了一个带有成员变量的类,并允许使用attr_reader读取它。
class TestClass
attr_reader :val
def initialize(value)
@val = value
end
end
现在,当我执行以下操作时,它似乎修改了@val的值,即使我只授予它读权限。
test = TestClass.new('hello')
puts test.val
test.val << ' world'
puts test.val
这返回
hello
hello world
这只是我在irb中做的一些测试的结果所以不确定是否总是这样
您实际上并没有编写val属性。您正在读取它并调用它的方法('<<'方法)。
如果你想要一个访问器来防止你所描述的那种修改,那么你可能需要实现一个返回@val副本的方法,而不是使用attr_reader。
赋值不同于修改,变量不同于对象。
test.val = "hello world"
将赋值给@val
实例变量(这是不工作的),而
test.val << " world"
将是@val
引用的对象的修改。
为什么没有赋值操作符允许我修改Ruby常量而没有编译器警告?是一个类似的问题,但讨论的是常量而不是实例变量。
只是对你的例子做了一点修改:
test = TestClass.new([])
现在你应该得到(用p代替put来获得内部视图):
[]
['hello']
这是一样的。你"读取"val,现在你可以对它做一些事情。在我的例子中,你向数组中添加了一些东西,在你的例子中,你向字符串中添加了一些东西。
读访问读取对象(该对象可以被修改),写访问更改属性(它替换它)。
也许你在找freeze
:
class TestClass
attr_reader :val
def initialize(value)
@val = value
@val.freeze
end
end
test = TestClass.new('hello')
puts test.val
test.val << ' world'
puts test.val
结尾:
__temp.rb:12:in `<main>': can't modify frozen string (RuntimeError)
hello
为了避免冻结变量的副作用,您可以复制value
:
class TestClass
attr_reader :val
def initialize(value)
@val = value.dup.freeze #dup to avoid to freeze the variable "value"
end
end
hello = 'hello'
test = TestClass.new(hello)
puts test.val
hello << ' world' #this is possible
puts test.val #this value is unchanged
test.val << ' world' #this is not possible
puts test.val
虽然这个看起来出乎意料,但这是完全正确的。让我解释一下。
attr_reader
和attr_writer
类宏方法为实例变量定义了"getter"one_answers"setter"方法。
如果没有"getter"方法,你就不能访问对象的实例变量,因为你不在该对象的上下文中。"setter"方法本质上是这样的:
def variable=(value)
@variable = value
end
由于实例变量指向具有一组方法本身的可变对象,如果您"获取"它并操作它,则理所当然会发生这些更改。您不需要使用上述setter方法来调用variable.<<(value)
。