如何测试Ruby对象是否不可变?

时间:2009-01-18 16:12:44

标签: ruby immutability

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

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

4 个答案:

答案 0 :(得分:6)

我找到了一种效率低下的方法:

class Object
  def primitive?
    begin
      self.dup
      false
    rescue TypeError
      true
    end
  end
end

答案 1 :(得分:4)

Ruby中没有原始对象。因此,无法以直接的方式检测到这一点。

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

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

答案 2 :(得分:2)

可变性的想法并不像Ruby在其他语言中那样适用。唯一不可变的对象是冻结的对象。您甚至可以将方法和实例变量添加到Fixnums。例如:

class Fixnum
  attr_accessor :name
end
1.name = "one"
2.name = "two"
很明显,绝大多数时候,人们不会在病态上为Fixnum添加属性,但重点是,没有解冻的对象是真正不可改变的。

如果你能想出一个你想要假定的类的大学列表是不可变的,你可以直接给它们一个返回true的immutable?()方法(和Object一个返回false的版本) 。但就像wvanbergen所说,确保你的物品副本不会改变的最好方法是用Marshal深层复制它。

答案 3 :(得分:1)

另一个区别:本机不可变对象无法冻结,但它们仍然会从冻结中返回false?

5.freeze.frozen? == false

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

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

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