先谈一下我对Span的看法, Span是指向任意连续内存空间的类型安全、内存安全的视图。,Span和Memory都是包装了可以在pipeline上使用的结构化数据的内存缓冲器,他们被设计用于在pipeline中高效传递数据。,这里面许多定语,值得我们细细揣摩:,1. 指向任意连续内存空间:支持托管堆,原生内存、堆栈, 这个可从Span的几个重载构造函数窥视一二。,2. 类型安全:Span 是一个泛型。,3. 内存安全: Span[1]是一个readonly ref struct数据结构,用于表征一段连续内存的关键属性被设置成只读readonly, 保证了所有的操作只能在这段内存内。,4. 视图:操作结果会直接体现到底层的连续内存。,至此我们来看一个简单的用法, 利用span操作指向一段堆栈空间。,从Slice切片源码可以看到,实质是利用原ptr & length 产生包含新的ptr & length的操作视图, ptr其实是指针的移动,也就是定位新的数据块, 但是终归是在原始数据块内部。,我们再细看Span的定义, 有几个关键词建议大家温故而知新。,从C#7.2开始,你可以将readonly作用在struct上,指示该struct不可改变。,span 被定义为readonly struct,内部属性自然也是readonly,从上面的分析和实例看我们可以针对Span表征的特定连续内存空间做内容更新操作;,如果想限制更新该连续内存空间的内容, C#提供了ReadOnlySpan类型, 该类型强调该块内存只读,也就是不存在Span 拥有的Fill,Clear等方法。,一线码农大佬写了文章讲述[使用span对字符串求和]的姿势,大家都说使用span能高效操作内存,我们对该用例BenchmarkDotNet压测。,压测解读:,对字符串运行时切分,不会利用驻留池,于是case1会分配大量小对象;,case2对底层字符串切片,虽然会产生不同的透视对象Span, 但是实际引用了的原始内存块的偏移区间, 不存在分配新内存。,从C#7.2开始,ref可以作用在struct,指示该类型被分配在堆栈上,并且不能转义到托管堆。,Span,ReadonlySpan 包装了对于任意连续内存快的透视操作,但是只能被存储堆栈上,不适用于一些场景,例如异步调用,.NET Core 2.1为此新增了Memory[4] , ReadOnlyMemory, 可以被存储在托管堆上,这个暂时按下不表。,最后用一张图总结, 本文成文,感谢[ yi念之间 ]大佬参与讨论。,[1] Span: https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Span.cs,[2] readonly strcut: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/struct#readonly-struct,[3] ref struct: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/struct,[4] Memory: https://docs.microsoft.com/en-us/dotnet/standard/memory-and-spans/memory-t-usage-guidelines
© 版权声明
文章版权归作者所有,未经允许请勿转载。