如果使用<<, Ruby attr_reader允许修改字符串变量



我遇到了一些奇怪的行为,想知道是否有人能证实我所看到的。

假设您创建了一个带有成员变量的类,并允许使用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_readerattr_writer类宏方法为实例变量定义了"getter"one_answers"setter"方法。

如果没有"getter"方法,你就不能访问对象的实例变量,因为你不在该对象的上下文中。"setter"方法本质上是这样的:

def variable=(value)
  @variable = value
end

由于实例变量指向具有一组方法本身的可变对象,如果您"获取"它并操作它,则理所当然会发生这些更改。您不需要使用上述setter方法来调用variable.<<(value)

最新更新