我最近开始使用F#进行“实际工作”,并重新发现了不可变数据结构的美妙之处,例如F#中的歧视联盟和记录。我还发现它们很容易从C#中使用,特别是因为它们不需要对F#运行时有任何直接依赖。但是,在表示这些结构中的列表时,我还没有找到理想的解决方案。
我的第一次尝试是将列表键入seq<'a> (在C#世界中是IEnume),它提供了一个很好的通用集合接口,而没有以ICollection<>的方式导出用于改变集合的任何方法。和它的朋友一样。但是,由于我无法控制有区别的联合或记录的构造函数,因此这些类型的实例的创建者可以提供IEnumerable<>。使用时可能更改或抛出的实现(例如LINQ表达式)。 IEnumerable的<>因此,不会向编译器提供任何帮助,以证明该值是不可变的,因此线程安全。
我目前的策略是使用F#列表类型,该类型确保了不可变集合,但是在F#运行时添加了依赖关系,并且在非F#项目中使用时看起来有些偏差。然而,它允许F#模式匹配,其中IEnumerable<>才不是。它也没有在列表的实际表示中给出任何选择,并且在某些情况下(例如原始值的大列表),F#列表表示并不真正适合。
我真正希望看到的是.NET中的不可变数组类型,表示与普通数组一样紧凑,但编译器保证不会发生变异。我会像C ++一样欢迎const,尽管它可能不太可能发生。与此同时,我还有其他选择吗?
答案 0 :(得分:7)
如果你想要的是一个不可变的数组实现,那么你可以使用类似下面的内容
public sealed class ImmutableArray<T> : IEnumerable<T>{
private readonly T[] m_array;
public T this[int index] {
get { return m_array[index]; }
}
public int Length {
get { return m_array.Length; }
}
public ImmutableArray(IEnumerable<T> enumerable) {
m_array = enumerable.ToArray();
}
// IEnumerable<T> implementation ommitted
}
答案 1 :(得分:2)
将您的普通数组包装在ReadOnlyCollection<T>
实例中:
var readOnlyData = new ReadOnlyCollection<TheType>(theRealCollection);
(注意,这不会复制底层集合,但会保留一个引用,并且会修改IList<T>
等成员以实现异常。)
答案 2 :(得分:1)
我建议在Eric Lippert's上查看immutability系列,这是非常有用的读物。关于immutable queue的第4部分将是一个很好的起点。
答案 3 :(得分:1)
Microsoft发布了Immutable collections nugget包的稳定版本。它还没有ImmutableArray。但是这些不可变数据结构应该非常有用。
http://blogs.msdn.com/b/dotnet/archive/2013/09/25/immutable-collections-ready-for-prime-time.aspx