C#中数组的不可变替代品是什么?

时间:2015-03-26 09:21:35

标签: c# immutability

C#中用于生产代码的数组的不可变替代品(至少在ImmutableArray处于测试阶段时)?

2 个答案:

答案 0 :(得分:7)

您应该提供更多详细信息,但有一个选项是System.Collections.ObjectModel.ReadOnlyCollection<>,它充当列表/数组。它可以像这样创建:

  var immutable1 = Array.AsReadOnly(new[] { 7, 9, 13, });
  var immutable2 = (new List<int> { 7, 9, 13, }).AsReadOnly();

它是IList<>的包装器。它不能转换为允许更改基础列表/数组的任何内容。但当然,可以通过反思达到基础对象。

如果其他人在其他地方发生了变异,那么&#34;内部&#34; list / array,将在ReadOnlyCollection<>包装器中重新链接。没有复制。

这是一个很浅的&#34;不变形式。如果单个项目可以变异(int不能),则包装器不会禁止它。


更便宜的事情就是使用IReadOnlyList<out T>中协变的接口T(一个额外的优势)。 List<T>T[]都实现此接口。所以:

  IReadOnlyList<int> immutable3 = new[] { 7, 9, 13, };
  IReadOnlyList<int> immutable4 = new List<int> { 7, 9, 13, };

然而,在这种情况下,消费者可以测试immutable3 as IList<int>,例如,或immutable3 as int[],并获得&#34;可变类型参考&#34;到可变对象(没有反射)。


但是,有一些不可变的类型(例如ImmutableList<>)不是&#34;在beta&#34;在 Microsoft.Immutable.Collections 。所以你可以考虑安装它,如果你真的需要它。它不是标准.NET库的一部分,因此您必须单独安装它。


详细说明我的意思&#34;浅&#34;,假设你有一个数组,其元素类型本身是可变的。然后,无论您使用上述哪种建议,人们仍然可以对条目进行调整。例如:

StringBuilder[] yourArray = { 
    new StringBuilder("All"),
    new StringBuilder("is"),
    new StringBuilder("good"),
    };

var immutable = ImmutableList<StringBuilder>.Empty.AddRange(yourArray);

immutable[0].Insert(0, "NOT ");
immutable[1].Clear();
immutable[2].Append(new string('!', 10000));

答案 1 :(得分:1)

仅仅使用ReadOnlyCollectionIReadOnlyList的缺点是它们提供的保证比不可变数组要弱得多。这些对象仅保证只读集合的​​持有者不会修改它。它们不保证集合不会被包含对底层集合的引用的代码修改。

具体来说,此类对象的使用者在从集合中读取时仍必须使用线程同步,因为无法保证底层集合的所有者在您通过集合读取时不忙于添加/删除集合中的项目你的只读镜头。或者您必须在消费者和生产者之间签署一份文件协议,表明该集合不会被修改。

如果你真的想要一个不可变数组并且由于某种原因你不能使用Immutable Collections包,那么只需确保源永远不会修改数组,而是在它想要进行更改时克隆它。将此与发布“只读”句柄相结合,以实现不可变数组。

LINQ让你很容易实现自己的,虽然它不是非常聪明。以不可变的方式删除数组中的一个项目的示例:

private int[] _array;

public void RemoveItem(int item)
{
    _array = _array.Where(x => x != item).ToArray();
}

/// <summary>Gets immutable array</summary>
public IReadOnlyList<int> Data { get { return _array; } }