新的C#Span <T>与ArraySegment <T>有何不同?

The*_*Cat 25 c# .net-core

我无法概念化C#中新Span的用法.

  1. 它取代了哪些结构?ArraySegment现在已经过时了吗?

  2. 以前不支持哪些功能?

  3. Span是C#Arrays的有效替代品吗?在哪些情况下是,在哪些情况下没有?

  4. 我何时使用ArraySegment而不是Span?

我试图了解我的编码样式将如何更改以有效使用新的Span.

Mar*_*zek 28

Span<T>不会取代任何东西.这是增值.它提供了对连续内存段的类型安全视图,可以以多种不同方式分配:作为托管阵列,基于堆栈的内存或非托管内存.

ArraySegment<T>仅限于托管数组.您不能使用它来包装在堆栈上分配的数据stackalloc.Span<T>允许你这样做.

ArraySegment<T>也没有提供底层数组的只读视图.ReadOnlySpan<T>给你这个.

Span<T>不应该替换数组.在一天结束时,它只是一个数据视图.必须以某种方式分配该数据,并且在托管世界中,在大多数情况下,分配将是数组分配.所以你仍然需要数组.

Span<T>如果您希望代码能够操作的不仅仅是数组,那么您应该使用它.例如,考虑一个解析库.现在,为了允许它使用数组,堆栈分配的内存和非托管内存,它必须在API中为每个内容提供多个入口点,并使用不安全的代码来实际操作数据.它也可能需要公开一个string基于API的API,供将数据分配为字符串的人使用.使用SpanReadOnlySpan您可以将所有逻辑合并到一个Span基于单一的解决方案中,该解决方案将适用于所有这些方案.

Span<T>绝对不会是每个人都经常使用的东西.它是.NET框架的一个高度专业化的部分,主要用于库作者和非常高性能的关键场景.例如,Kestrel,ASP.NET Core后面的Web服务将从移动到中获得很多性能优势,Span<T>因为例如解析请求可以使用Span<T>和堆栈分配的内存来完成,这对GC没有任何压力.但是,编写基于ASP.NET Core的网站和服务并不一定非必须使用它.

  • Span&lt;T&gt; 是仅堆栈结构(相当新且棘手的 lang 功能)。您不能将它保存在某个非堆栈类或结构的字段中。所以 Span&lt;T&gt; 非常强大,但在使用上也非常有限。另一方面,ArraySegment&lt;T&gt; 非常简单且有用,只要您需要“缓冲区/偏移量/计数”三位一体且无法使用 Span&lt;T&gt;。 (3认同)
  • 很好的扩展 - 从您的描述看来,与 ArraySegment&lt;T&gt; 相比,Span&lt;T&gt; 和 ReadOnlySpan&lt;T&gt; 提供了功能的超集...我什么时候会使用 ArraySegment&lt;T&gt; 而不是 Span&lt;T&gt;?因为我似乎永远不会。 (2认同)

Vad*_* S. 6

在决定是否使用 Span 时还要考虑适用于 C# 中的ref 结构的限制:

https://docs.microsoft.com/en-us/dotnet/api/system.span-1?view=netcore-2.2

Span 是一个ref 结构,它在堆栈上而不是在托管堆上分配。Ref struct 类型有许多限制以确保它们不能被提升到托管堆,包括它们不能被装箱,它们不能被分配给 Object、dynamic 或任何接口类型的变量,它们可以'不是引用类型中的字段,并且它们不能跨 await 和 yield 边界使用。此外,调用 Equals(Object) 和 GetHashCode 这两个方法会抛出 NotSupportedException。

重要的

由于它是仅堆栈类型,因此 Span 不适用于许多需要在堆上存储对缓冲区的引用的场景。例如,对于进行异步方法调用的例程来说,情况确实如此。对于此类场景,您可以使用免费的 System.Memory 和 System.ReadOnlyMemory 类型。

对于表示不可变或只读结构的跨度,请使用 System.ReadOnlySpan。

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/ref?view=netcore-2.2#ref-struct-types

将 ref 修饰符添加到结构声明定义该类型的实例必须被堆栈分配。换句话说,这些类型的实例永远不能作为另一个类的成员在堆上创建。此功能的主要动机是 Span 和相关结构。

将 ref struct 类型保留为堆栈分配变量的目标引入了编译器对所有 ref struct 类型强制执行的几条规则。

  • 你不能装箱一个 ref 结构。
  • 您不能将 ref struct 类型分配给类型为 object、dynamic 或任何接口类型的变量。
  • ref 结构类型不能实现接口。
  • 您不能将 ref struct 声明为类或普通 struct 的成员
  • 您不能在异步方法中声明属于 ref 结构类型的局部变量。您可以在返回 Task、Task 或 Task-like 类型的同步方法中声明它们。
  • 您不能在迭代器中声明 ref struct 局部变量。
  • 您不能在 lambda 表达式或局部函数中捕获 ref struct 变量。
  • 这些限制确保您不会意外地以可能将其提升到托管堆的方式使用 ref 结构。

您可以组合修饰符将结构声明为只读引用。readonly ref struct 结合了 ref struct 和 readonly struct 声明的优点和限制。


Fur*_*bay 5

MSDN杂志》:定义跨度的方式可以使操作与对数组一样高效:对跨度建立索引不需要进行计算即可确定指针的起始位置及其起始偏移量,因为ref字段本身已经封装了两者。(相比之下,ArraySegment具有一个单独的offset字段,这使得索引和传递都更加昂贵。)

另外,虽然Array实现了IEnumerable,但Span却没有。