Closed gaufung closed 1 month ago
Span<T>
是高性能 C#
代码的秘诀之一,.NET
社区大佬 Stephen Toub
深入探究了什么是 Span
并且从头完成一个简易版的实现。
首先 Span<T>
要解决什么问题?假设我们现在有一个方法是这样的,
private int Sum(int[] array)
{
int sum = 0;
foreach(var val in array) sum += val;
return sum;
}
如果 Sum
方法的是求和数组的部分内容,那么方法的签名需要修改成这样
private int Sum(int[] array, int offset, int length)
{
int sum = 0;
for(int i = offset; i < length; i++) sum += array[offset+i];
return sum;
}
这样会带来一个问题,就是这个方法只支持 int[]
数据类型,而 C#
中有很多类型都是表示连续的一段空间,比如 List
。所以 Span
这个类型结构就被提出来了,如果仅仅是表示一段连续空间,Span
并没有什么特殊之处,C/C++
中的指针,或者 C#
中的 unsafe
代码块也能够完成同样的工作,但是 Span
是内存安全的类型,而且还是一个值类型。
readonly ref struct MySpan<T>
{
private readonly ref T _reference;
private readonly int _length;
public MySpan(T[] array)
{
_reference = ref MemoryMarshal.GetArrayDataReference(array);
_length = array.Length;
}
public MySpan(ref T reference)
{
_reference = ref reference;
_length = 1;
}
public MySpan(ref T reference, int length)
{
_reference = ref reference;
_length = length;
}
public ref T this[int index]
{
get
{
if (index < 0 || index >= _length)
{
throw new IndexOutOfRangeException();
}
return ref Unsafe.Add(ref _reference, index);
}
}
public MySpan<T> Slice(int offset)
{
if (offset < 0 || offset >= _length)
{
throw new IndexOutOfRangeException();
}
return new MySpan<T>(ref Unsafe.Add(ref _reference, offset), _length - offset);
}
}
ref struct
表明它只能用在方法中,而不能作为一个类的成员ref T _reference
指向了连续空间的第一个元素 ref T this[int index]
说明连续空间的每个元素获取都是引用类型Unsafe.Add
该方法避免了访问非法内存
https://www.youtube.com/watch?v=5KdICNWOfEQ