在.NET中表示不可变列表的最佳方法是什么?

时间:2010-08-31 18:31:07

标签: .net list f# immutability

我最近开始使用F#进行“实际工作”,并重新发现了不可变数据结构的美妙之处,例如F#中的歧视联盟和记录。我还发现它们很容易从C#中使用,特别是因为它们不需要对F#运行时有任何直接依赖。但是,在表示这些结构中的列表时,我还没有找到理想的解决方案。

我的第一次尝试是将列表键入seq<'a> (在C#世界中是IEnume),它提供了一个很好的通用集合接口,而没有以ICollection<>的方式导出用于改变集合的任何方法。和它的朋友一样。但是,由于我无法控制有区别的联合或记录的构造函数,因此这些类型的实例的创建者可以提供IEnumerable<>。使用时可能更改或抛出的实现(例如LINQ表达式)。 IEnumerable的<>因此,不会向编译器提供任何帮助,以证明该值是不可变的,因此线程安全。

我目前的策略是使用F#列表类型,该类型确保了不可变集合,但是在F#运行时添加了依赖关系,并且在非F#项目中使用时看起来有些偏差。然而,它允许F#模式匹配,其中IEnumerable<>才不是。它也没有在列表的实际表示中给出任何选择,并且在某些情况下(例如原始值的大列表),F#列表表示并不真正适合。

我真正希望看到的是.NET中的不可变数组类型,表示与普通数组一样紧凑,但编译器保证不会发生变异。我会像C ++一样欢迎const,尽管它可能不太可能发生。与此同时,我还有其他选择吗?

4 个答案:

答案 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。但是这些不可变数据结构应该非常有用。

  • ImmutableList
  • ImmutableDictionary
  • ImmutableSortedDictionary
  • ImmutableHashSet
  • ImmutableSortedSet
  • ImmutableStack
  • ImmutableQueue

http://blogs.msdn.com/b/dotnet/archive/2013/09/25/immutable-collections-ready-for-prime-time.aspx