内容目录
因为结构体或者值类型在传递时,是值复制,导致传递后修改其值,原先的值不会发生改变。
即使使用 ref
做参数,也没法改变传递数组是值复制的问题。
阅读 .NET 源码是发现了 .NET 6 的一个 API,可以很方便完成这个任务,让结构体像引用类型一样。
using System.Runtime.InteropServices;
public class Progarm
{
static void Main()
{
var a = new A[100];
var span = a.AsSpan();
ref A c = ref MemoryMarshal.GetReference(span);
c.B = "1111";
}
}
public struct A
{
public string B
{
get; set;
}
}
如果要获取指定位置的指针,可以使用:
ref A d = ref Unsafe.Add(ref c, 1);
static void Main()
{
var a = new A[100];
var span = a.AsSpan();
ref A c = ref MemoryMarshal.GetReference(span);
c.B = "0";
ref A d = ref Unsafe.Add(ref c, 1);
d.B = "1";
ref A e = ref Unsafe.Add(ref c, 2);
e.B = "2";
}
这样可以访问基于 c
开始的,指定位置的值。
.NET 源代码就使用这种方式反转结构体数组(Span<T>
) 的内容:
public static void Reverse<T>(this Span<T> span)
{
ref T p = ref MemoryMarshal.GetReference(span);
int i = 0;
int j = span.Length - 1;
while (i < j)
{
T temp = Unsafe.Add(ref p, i);
Unsafe.Add(ref p, i) = Unsafe.Add(ref p, j);
Unsafe.Add(ref p, j) = temp;
i++;
j--;
}
}
通过 Span 获取结构体指针的原理:
public static ref T GetReference<T>(Span<T> span)
{
if (span.Pinnable == null)
unsafe { return ref Unsafe.AsRef<T>(span.ByteOffset.ToPointer()); }
else
return ref Unsafe.AddByteOffset<T>(ref span.Pinnable.Data, span.ByteOffset);
}
源码位置:System.Runtime.InteropServices
、MemoryMarshal
。
文章评论