我无法概念化the new Span in C#的用法。
它取代了哪些构造? ArraySegment现在已经过时了吗?
以前不支持哪些功能?
Span是C#Arrays的有效替代品吗?在哪些情况下是,在哪些情况下没有?
我何时使用ArraySegment而不是Span?
我试图了解我的编码风格需要如何更改才能有效利用新的Span。
答案 0 :(得分:24)
Span<T>
不会取代任何内容。这是增值。它提供了对连续内存段的类型安全视图,可以以多种不同方式分配:作为托管阵列,基于堆栈的内存或非托管内存。
ArraySegment<T>
仅限于托管数组。您不能使用它来使用stackalloc
包装在堆栈上分配的数据。 Span<T>
允许您这样做。
ArraySegment<T>
也不提供底层数组的只读视图。 ReadOnlySpan<T>
为您提供了这一点。
Span<T>
不应该替换数组。在一天结束时,它只是一个数据视图。必须以某种方式分配该数据,并且在托管世界中,在大多数情况下,分配将是数组分配。所以你仍然需要数组。
如果您希望代码能够操作的不仅仅是数组,那么您应该使用Span<T>
。例如。考虑一个解析库。现在,为了允许它使用数组,堆栈分配的内存和非托管内存,它必须在API中为每个内容提供多个入口点,并使用不安全的代码来实际操作数据。它也可能需要公开一个基于string
的API,供将数据分配为字符串的人使用。使用Span
和ReadOnlySpan
,您可以将所有逻辑合并到基于Span
的单个解决方案,该解决方案将适用于所有这些方案。
Span<T>
绝对不是每个人都经常使用过的东西。它是.NET框架的一个高度专业化的部分,主要用于库作者和非常高性能的关键场景。例如。 Kestrel,ASP.NET Core背后的Web服务将从转移到Span<T>
获得很多性能优势,例如解析请求可以使用Span<T>
和堆栈分配的内存来完成,这对GC没有任何压力。但是,您编写基于ASP.NET Core的网站和服务并不一定非必须使用它。
答案 1 :(得分:5)
从MSDN Magazine开始:Span的定义方式使得操作可以像在数组上一样高效:索引到span中不需要计算来确定指针的起点及其起始偏移量,如ref字段本身已经封装了两者。 (相比之下,ArraySegment有一个单独的偏移字段,使得索引和传递它的成本更高。)
此外,当Array实现IEnumerable
时,Span不会。
答案 2 :(得分:1)
在决定是否使用Span时,也要考虑到C#中 ref结构的限制:
https://docs.microsoft.com/en-us/dotnet/api/system.span-1?view=netcore-2.2
Span是一个 ref结构,它在堆栈上分配,而不是分配 在托管堆上。 引用结构类型对 确保不能将它们提升为托管堆,包括 他们不能装箱,他们不能分配给类型的变量 动态或任何接口类型的对象,它们不能是 引用类型,它们不能在等待和收益中使用 边界。另外,调用两个方法Equals(Object)和 GetHashCode,引发NotSupportedException。
重要
因为它是仅堆栈类型,所以Span不适合许多 需要在堆上存储对缓冲区的引用的方案。这个 例如,对于进行异步方法调用的例程,它是正确的。 在这种情况下,您可以使用免费的System.Memory和 System.ReadOnlyMemory类型。
对于表示不可变或只读结构的跨度,请使用 System.ReadOnlySpan。
将ref修饰符添加到结构声明中可定义实例 这种类型的必须堆栈分配。换句话说, 这些类型永远不能作为另一个的成员在堆上创建 类。此功能的主要动机是跨度和相关性 结构。
将引用结构类型保留为堆栈分配变量的目标 引入了几个规则,这些规则由编译器针对所有引用结构强制执行 类型。
- 您不能装箱引用结构。
- 您不能将引用结构类型分配给类型为object,dynamic或任何接口类型的变量。
- 引用结构类型无法实现接口。
- 您不能将引用结构声明为类或普通结构的成员。
- 您不能在异步方法中声明作为引用结构类型的局部变量。您可以在返回Task,Task或类似Task的类型的同步方法中声明它们。
- 您不能在迭代器中声明引用结构局部变量。
- 您无法在lambda表达式或本地函数中捕获引用结构变量。
- 这些限制确保您不会意外地将引用结构提升为托管堆。
您可以组合使用修饰符以将结构声明为只读引用。一种 只读ref结构结合了ref的优点和限制 结构体和只读结构体声明。