1. Span<T>的特性
- system.span<T>在.net core 2.0版本引入
- 它适用于对连续内存的操作,而不产生新的内存分配,比如数组、字符串、堆外内存
- 类型为ref struct,不能作为参数传递,不能被装箱(不能作为类的字段),不能作为async的返回值,只能是现用现声明,用来对原始数组操作
- 可以通过stackalloc在栈上分配内存,不涉及堆分配和垃圾回收
- 因为不涉及堆内存分配,所以性能比较高
1.1 Span<T> 的一些典型用法示例:
- 对已有的数组进行切片操作,无需创建新数组:
int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };// 获取一个数组切片slice,现在指向 numbers 的子区域,包含元素 2, 3, 4, 5, 6
Span<int> slice = numbers.AsSpan().Slice(start: 2, length: 5);
- 在不需要副本的情况下操作字符串:
string str = "Hello, World!";
//获取字符串的一个切片,span 现在指向字符串 "World"
//注意:这里使用 ReadOnlySpan<char> 因为字符串是不可变的
ReadOnlySpan<char> span = str.AsSpan().Slice(start: 7, length: 5);
- 处理从文件或网络读取的数据缓冲区:
-
//假设这是从文件或网络读取的数据 byte[] buffer = new byte[1024];//现在可以使用 bufferSpan 进行读取和写入操作,而不需要复制数据 Span<byte> bufferSpan = buffer.AsSpan();
- 栈分配数组(通过 stackalloc)
//在栈上分配内存,不涉及堆分配或垃圾回收
Span<int> stackAllocatedArray = stackalloc int[10];
for (int i = 0; i < stackAllocatedArray.Length; i++)
{ stackAllocatedArray[i] = I;
}
- 图像处理和音频缓冲操作等领域,需要高性能内存操作:
-
//假设有一个表示图像像素的 byte 数组 byte[] imagePixels = new byte[width * height * bytesPerPixel]; Span<byte> imageSpan = imagePixels.AsSpan(); // 对图像进行处理,例如反转颜色 for (int i = 0; i < imageSpan.Length; i += bytesPerPixel) { imageSpan[i] = (byte)(255 - imageSpan[i]); // R imageSpan[i + 1] = (byte)(255 - imageSpan[i + 1]); // G imageSpan[i + 2] = (byte)(255 - imageSpan[i + 2]); // B // 如果是 RGBA 格式,还需要处理 Alpha 通道 }
Span<T>的这些用法展示了它如何提供对内存的高效访问,同时保持类型安全。由于 Span<T>是 `ref struct`,它具有一些限制,例如不能被装箱、不能作为异步方法的返回值,也不能存储在堆上,这些限制有助于保持其性能优势。
2. ref struct的特性
- 特殊的值类型
- 不能被装箱,不能作为类的字段,不能作为异步方法的返回值
- 不涉及堆内存分配,性能比较高
2.1 ref struct 与 值类型的区别
- 值类型可以被装箱,可以作为类的字段
- ref struct 不能被装箱,不可以作为类的字段