必须将ArraySegment强制转换为IList以使用Index对其进行迭代。有没有办法隐藏不相关的IList方法?

时间:2018-10-01 00:58:24

标签: c# arrays list

要能够像对待数组一样直接处理ArraySegment,必须使用“ as”将其强制转换为IList。在此处将其描述为正确的行为:

use of ArraySegment class?

在这里:

dotNet ArraySegment Struct

具体来说,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不支持索引。如果有人对这个问题有任何建议,我会很高兴。

2 个答案:

答案 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;
}