5

有没有一种简单的方法来测试一个对象是否是不可变的(数字,零)(数组,哈希,对象)?换句话说,它会被其他代码的副作用改变吗?

动机:我想创建一个版本化的值存储,但有些数据是数组。一些数组将存储自定义对象,我可以通过存储“in”属性并搜索它来反转关系。但我也希望能够存储符号数组、其他数组等。

4

4 回答 4

6

我发现了一种低效的方法:

class Object
  def primitive?
    begin
      self.dup
      false
    rescue TypeError
      true
    end
  end
end
于 2009-01-18T16:35:56.473 回答
4

Ruby 中没有原始对象。因此,这不能以直接的方式检测到。

您不能简单地将 Marshal 或 YAML 用于您的版本化存储吗?然后,您将免费加载和保存所有对象类型。为什么要重新发明轮子?

我不知道你想要实现什么,但是查看 YAML 的源代码可能会很有趣,看看他们如何处理这个问题。Ruby YAML 编码实现只是to_yaml为所有相关类实现了该方法。请参阅yaml/rubytypes.rb

于 2009-01-18T16:53:57.013 回答
2

可变性的概念在 Ruby 中的应用方式与在其他语言中的应用方式不同。唯一不可变的对象是冻结的对象。您甚至可以将方法和实例变量添加到 Fixnums。例如:

class Fixnum
  attr_accessor :name
end
1.name = "one"
2.name = "two"

显然,绝大多数时候,人们不会病态到给 Fixnum 添加属性,但关键是,没有一个解冻对象是真正不可变的。

如果你能想出一个你想假设是不可变的类的规范列表,你可以通过并给它们一个immutable?()返回 true 的方法(和 Object 一个返回 false 的版本)。但就像 wvanbergen 所说,确保您的对象副本不会更改的最佳方法是使用 Marshal 对其进行深度复制。

于 2009-01-18T20:53:29.793 回答
1

另一个区别:本机不可变对象不能被冻结,但它们仍然从冻结​​返回 false?

5.freeze.frozen? == false

冻结不会引发异常(与 dup 不同)但是它会(永久!)修改可变对象。

我发现我可以(至少在当前状态下)安排我的应用程序处理冻结的对象,如果我尝试直接修改它们,ruby 会给我一个例外。但是freeze 只影响 object 的第一层,存储在其中的数组等仍然可以修改。

这仅适用于 1.8 - 5.frozen?在 ruby​​1.9 中返回 true(但在 irb1.9 中不返回)

于 2009-01-18T20:44:48.843 回答