是否有一种简单的方法来测试对象是否是不可变的(数字,零)或不是(数组,哈希,对象)?换句话说,它可以被其他代码的副作用改变吗?
动机:我想创建一个版本化的值存储,但有些数据是数组。一些数组将存储自定义对象,我可以通过存储“in”属性并搜索它来反转关系。但我也希望能够存储符号数组,其他数组等。
答案 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中)