C#中用于生产代码的数组的不可变替代品(至少在ImmutableArray处于测试阶段时)?
答案 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)
仅仅使用ReadOnlyCollection
或IReadOnlyList
的缺点是它们提供的保证比不可变数组要弱得多。这些对象仅保证只读集合的持有者不会修改它。它们不保证集合不会被包含对底层集合的引用的代码修改。
具体来说,此类对象的使用者在从集合中读取时仍必须使用线程同步,因为无法保证底层集合的所有者在您通过集合读取时不忙于添加/删除集合中的项目你的只读镜头。或者您必须在消费者和生产者之间签署一份文件协议,表明该集合不会被修改。
如果你真的想要一个不可变数组并且由于某种原因你不能使用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; } }