ArraySegment < T > 类的用途是什么?

我只是在子类化 MessageEncoder类时偶然发现了 ArraySegment<byte>类型。

我现在知道它是给定数组的一个段,带有偏移量,不可枚举,并且没有索引器,但是我仍然不能理解它的用法。谁能举个例子解释一下?

56518 次浏览
  1. IO 类的缓冲区分区——同时使用相同的缓冲区 读写操作,并具有一个 你可以传递的单个结构 描述了你的整个行动。
  2. 设置函数-从数学上讲,你可以表示任何 使用这个新的 结构。这基本上意味着你 可以创建数组的分区, 但你不能代表所有的可能性 和所有的偶数。注意,电话 由 The1提出的挑逗可能会 被优雅地解决了 数组段分区和树 structure. The final numbers could have been written out by traversing 首先是树的深度 在... 方面是一个理想的情况 我相信记忆和速度。
  3. 多线程-您现在可以产生多个线程来操作 使用分段的同一数据源 数组作为控制门。循环 现在可以使用离散计算 很容易就被外包出去了 that the latest C++ compilers are 开始做一个代码优化 step.
  4. UI Segmentation - Constrain your UI displays using segmented 结构。现在可以存储 表示数据页的结构 可以快速应用于 显示功能。单个连续的 可以使用数组来显示 离散的视图,甚至是分层的 结构中的节点等 TreeView by segmenting a linear data 存储到节点集合段中。

在本例中,我们将研究如何使用原始数组、 Offset 和 Count 属性,以及如何循环遍历 ArraySegment 中指定的元素。

using System;


class Program
{
static void Main()
{
// Create an ArraySegment from this array.
int[] array = { 10, 20, 30 };
ArraySegment<int> segment = new ArraySegment<int>(array, 1, 2);


// Write the array.
Console.WriteLine("-- Array --");
int[] original = segment.Array;
foreach (int value in original)
{
Console.WriteLine(value);
}


// Write the offset.
Console.WriteLine("-- Offset --");
Console.WriteLine(segment.Offset);


// Write the count.
Console.WriteLine("-- Count --");
Console.WriteLine(segment.Count);


// Write the elements in the range specified in the ArraySegment.
Console.WriteLine("-- Range --");
for (int i = segment.Offset; i < segment.Count+segment.Offset; i++)
{
Console.WriteLine(segment.Array[i]);
}
}
}

数组段结构——他们在想什么?

它是一个很小的二进制结构,除了保存对数组的引用和存储索引范围之外什么也不做。有点危险,注意它不会复制数组数据,也不会以任何方式使数组成为不可变的,或者表示不可变的需要。更典型的编程模式是只保留或传递数组和长度变量或参数,就像在。NET BeginRead ()方法,字符串。SubString () ,编码。等等。

它在。NET 框架,除了看起来像是一个特别的微软程序员,工作在网络套接字和 WCF 喜欢它。这可能是正确的指导,如果你喜欢它,然后使用它。它确实在里面躲猫猫了。NET 4.6,增加了内存流。TryGetBuffer ()方法使用它。我假设他们更喜欢有两个 out参数。

一般来说,更普遍的概念 切片是高度的愿望清单的主体。像 Mads Torgersen 和 Stephen Toub 这样的 NET 工程师。后者在不久前启动了 array[:]语法提案,您可以看到他们在 这个 Roslyn 页面中一直在考虑的内容。我认为获得 CLR 支持是最终的关键。C # 版本7 afaik 正在积极考虑这个问题,请密切关注 系统,切片

更新: 死链接,这是在7.2版本中作为 Span发布的。

Update2: 在 C # 8.0版本中提供更多支持,包括 Range 和 Index 类型以及 Slice ()方法。

ArraySegment<T>.NET 4.5 + 和 .NET 核心中变得更加有用,因为它现在实现了:

  • IList<T>
  • ICollection<T>
  • IEnumerable<T>
  • IEnumerable
  • IReadOnlyList<T>
  • IReadOnlyCollection<T>

.NET 4版本没有实现任何接口。

这个类现在可以参与到 LINQ 的奇妙世界中,所以我们可以做一些常见的 LINQ 事情,比如查询内容,在不影响原始数组的情况下反转内容,获得第一个条目,等等:

var array = new byte[] { 5, 8, 9, 20, 70, 44, 2, 4 };
array.Dump();
var segment = new ArraySegment<byte>(array, 2, 3);
segment.Dump(); // output: 9, 20, 70
segment.Reverse().Dump(); // output 70, 20, 9
segment.Any(s => s == 99).Dump(); // output false
segment.First().Dump(); // output 9
array.Dump(); // no change

What's about a wrapper class? Just to avoid copy data to temporal buffers.

public class SubArray<T> {
private ArraySegment<T> segment;


public SubArray(T[] array, int offset, int count) {
segment = new ArraySegment<T>(array, offset, count);
}
public int Count {
get { return segment.Count; }
}


public T this[int index] {
get {
return segment.Array[segment.Offset + index];
}
}


public T[] ToArray() {
T[] temp = new T[segment.Count];
Array.Copy(segment.Array, segment.Offset, temp, 0, segment.Count);
return temp;
}


public IEnumerator<T> GetEnumerator() {
for (int i = segment.Offset; i < segment.Offset + segment.Count; i++) {
yield return segment.Array[i];
}
}
} //end of the class

例如:

byte[] pp = new byte[] { 1, 2, 3, 4 };
SubArray<byte> sa = new SubArray<byte>(pp, 2, 2);


Console.WriteLine(sa[0]);
Console.WriteLine(sa[1]);
//Console.WriteLine(b[2]); exception


Console.WriteLine();
foreach (byte b in sa) {
Console.WriteLine(b);
}

Ouput:

3
4


3
4

The ArraySegment is MUCH more useful than you might think. Try running the following unit test and prepare to be amazed!

    [TestMethod]
public void ArraySegmentMagic()
{
var arr = new[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};


var arrSegs = new ArraySegment<int>[3];
arrSegs[0] = new ArraySegment<int>(arr, 0, 3);
arrSegs[1] = new ArraySegment<int>(arr, 3, 3);
arrSegs[2] = new ArraySegment<int>(arr, 6, 3);
for (var i = 0; i < 3; i++)
{
var seg = arrSegs[i] as IList<int>;
Console.Write(seg.GetType().Name.Substring(0, 12) + i);
Console.Write(" {");
for (var j = 0; j < seg.Count; j++)
{
Console.Write("{0},", seg[j]);
}
Console.WriteLine("}");
}
}

You see, all you have to do is cast an ArraySegment to IList and it will do all of the things you probably expected it to do in the first place. Notice that the type is still ArraySegment, even though it is behaving like a normal list.

OUTPUT:

ArraySegment0 {0,1,2,}
ArraySegment1 {3,4,5,}
ArraySegment2 {6,7,8,}

简而言之: 它保持对数组的引用,允许对单个数组变量有多个引用,每个变量具有不同的范围。

In fact it helps you to use and pass sections of an array in a more structured way, instead of having multiple variables, for holding start index and length. Also it provides collection interfaces to work more easily with array sections.

例如,下面的两个代码示例执行相同的操作,一个使用 ArraySegment,另一个不使用 ArraySegment:

        byte[] arr1 = new byte[] { 1, 2, 3, 4, 5, 6 };
ArraySegment<byte> seg1 = new ArraySegment<byte>(arr1, 2, 2);
MessageBox.Show((seg1 as IList<byte>)[0].ToString());

还有,

        byte[] arr1 = new byte[] { 1, 2, 3, 4, 5, 6 };
int offset = 2;
int length = 2;
byte[] arr2 = arr1;
MessageBox.Show(arr2[offset + 0].ToString());

显然,第一个代码片段更可取,特别是当您想要将数组段传递给函数时。