要能够像对待数组一样直接处理ArraySegment,必须使用“ as”将其强制转换为IList。在此处将其描述为正确的行为:
在这里:
具体来说,Microsoft文档说:
如果要通过ArraySegment中的索引检索元素 对象,则必须将其强制转换为IList对象并检索它,或者 通过使用IList.Item [Int32]属性对其进行修改。下列 示例检索ArraySegment对象中的元素,该对象 分隔字符串数组的一部分。
令人困惑的是IList具有类似“ Remove”和“ RemoveAt”的方法。您可能希望它们在转换为List的arraysegment上不起作用。我对此进行了测试,实际上调用Remove会引发运行时错误。但是编译器没有解决问题。
我很惊讶Microsoft在ArraySegment的设计中认为这种可接受的行为。
我试图集思广益包装类或某种方式来隐藏不应在ArraySegment上作为List调用的List方法。但是我什么都没想。
有人对如何解决此问题有建议吗?
编辑:建议使用IReadOnlyList代替IList。 IReadOnlyList导致List完全只读,从而防止您修改存储在基础数组中的元素的值。我希望能够修改原始数组的值。我只是不想写list.Remove()或list.Add(),因为它显然是错误的,并且编译器不应该允许它。
对于任何可能建议使用Span作为替代选择的人:
我知道Span,但是Span当前在.NET Framework和Standard中有局限性。具体来说,它只能用作局部变量,因此不能传递给其他方法。
老实说,我实际上认为Microsoft的IEnumerable层次结构还有点不足-我无法找出任何方法来制作像List这样的可索引序列,而没有提供Add / Remove功能。 ICollection不支持索引。如果有人对这个问题有任何建议,我会很高兴。
答案 0 :(得分:1)
事实证明,在.NET 4.7.2中,ArraySegment<T>
不会公开索引器,除非将其转换为IList<T>
接口,但在.NET Core 2.1中会显示。 >
您可以转换为IReadOnlyList<T>
界面;请注意,这不会阻止您更改包含的对象本身是否可变:
IReadOnlyList<T>
代表一个列表,其中列表元素的数量和顺序为只读。列表元素的内容不能保证是只读的。
因此,它仅保证集合本身(容器)是不可变的(没有添加,删除,替换)。您无法替换元素,因为索引器没有设置器。
还有ReadOnlyCollection<T>
包装器类(添加using System.Collections.ObjectModel;
)。索引器上也没有设置器。
如果这些都不适合您,并且您想实现包装器,只需创建一个在构造函数中使用IList<T>
的类,然后实现您自己的索引器。我还将实现IEnumerable<T>
,以便获得LINQ支持,并且还将使其与foreach循环一起使用。
// Add these...
using System.Collections;
using System.Collections.Generic;
//...
public class MyWrapper<T> : IEnumerable<T>
{
private IList<T> _internalList;
public MyWrapper(IList<T> list)
{
_internalList = list;
}
public int Count => _internalList.Count;
// the indexer
public T this[int index]
{
get => _internalList[index];
set => _internalList[index] = value;
}
public IEnumerator<T> GetEnumerator()
{
return _internalList.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
然后将数组段传递给构造函数(不强制转换)。
P.S。如果这是一些内部代码(仅在少数几个地方使用),那么编写包装程序可能会或可能不会造成麻烦;只要明确意图是什么,就可以使用IList<T>
。
此外,如果您不介意仅对数组的副本进行操作,该操作仅限于数组段所定义的范围,并且如果不必担心复制操作成本,则可以在段上调用ToArray或ToList扩展方法。但是我怀疑您想更新原始数组,而只需要在一个段中添加一个“窗口”即可。
答案 1 :(得分:0)
您仍然可以使用ArraySegment
,这将防止通过.Array
属性通过索引添加/删除项和访问/更新项
var numbers = new[] { 1, 2, 3, 4 };
var segment = new ArraySegment<int>(numbers);
segment.Array[2] = 101;
// segment contains now { 1, 2, 101, 4}
您始终可以创建扩展方法以提高可读性
public static void UpdateAt<T>(this ArraySegment<T> segment, int index, T value)
{
segment.Array[index] = value;
}