最近在学习.NET开源IO的源码,发现用的比较多的一个特性,那就是Span<T>,那么Span特性具体是做什么的呢?

Span<T> 表示任意内存的连续区域。 Span<T>实例通常用于保存数组或数组的一部分的元素。但是,与数组不同, Span<T> 实例可以指向堆栈上托管的内存、本机内存或内存。Span<Byte>
var array = new byte[1000000];var arraySpan = new Span<byte>(array);//装载到spanbyte data = 0;for (int i = 0; i < arraySpan.Length; i++){arraySpan[i] = data++;}int arraySum = 0;foreach (var item in arraySpan){arraySum += item;}Console.WriteLine("span结果是: {0}", arraySum);//span结果是: 127493856
var native = Marshal.AllocHGlobal(100000);Span<byte> nativeSpan;unsafe{nativeSpan = new Span<byte>(native.ToPointer(), 100);}byte data = 0;for (int ctr = 0; ctr < nativeSpan.Length; ctr++)nativeSpan[ctr] = data++;int nativeSum = 0;foreach (var value in nativeSpan)nativeSum += value;Console.WriteLine("span结果是: {0}", nativeSum );Marshal.FreeHGlobal(native);//span结果是: 127493856
Span<T> 和切片与数组
Span<T> 包括两个重载 Slice 的方法,该方法从从指定索引开始的当前范围外形成切片。这使得可以将数据 Span<T> 视为一组逻辑区块,这些区块可由数据处理管道的一部分根据需要进行处理,且性能影响最小。相当于Substring,但Substring会创建一个新的字符串,并且用原来的字符串复制到新的字符串。
我们来对比一下性能:
var str = "love.NET";var strSpan = str.AsSpan();var sw = new Stopwatch();sw.Start();for (var j = 0; j < 100000; j++){var strs= strSpan.Slice(4);}sw.Stop();Console.WriteLine("Span耗时:" + sw.ElapsedMilliseconds);// Span<T> 操作 ReadOnlySpan<T>来消除此分配和复制操作sw.Restart();for (var j = 0; j < 100000; j++){var strs = str.Substring(4);}sw.Stop();Console.WriteLine("Substring耗时:" + sw.ElapsedMilliseconds);

看出差距了吧。
Span<T>还可以包装整个数组,而且还支持整个数组切片,因此可以指定数组的连续范围。我们来看效果。
// 字符串数组var strs = new string[] { "java", "c++", "c#", "go","python" };var slice = new Span<string>(strs, 2, 2);for (int ctr = 0; ctr < slice.Length; ctr++)slice[ctr] += "牛";// Examine the original array values.foreach (var value in strs)Console.Write($"{value} ");//结果如下:
