Span<T> 是否可以在没有固定表达式的情况下指向固定大小的缓冲区?
Is a Span<T> pointing to Fixed Sized Buffers without a fixed expression possible?
我使用的是 .NET Core 2.1 和语言标准 7.3。我希望在不获取指向它的指针的情况下引用固定缓冲区。目前可以吗?
public unsafe struct InteropStruct
{
private fixed byte dataField[32];
public Span<byte> Data
{
get
{
//return a span referencing the private field without a fixed statement
}
}
}
我知道 Span 目前能够通过垃圾回收来跟踪托管数组,所以我没有看到任何东西阻止它以类似的方式跟踪固定缓冲区。
如果不可能,如果我确实使用这样的固定语句会发生什么:
public unsafe struct InteropStruct
{
private fixed byte dataField[32];
public Span<byte> Data
{
get
{
fixed (byte* ptr = dataField)
{
return new Span<byte>(ptr, 32);
}
}
}
}
如果结构被包装在一个对象中或 class 在堆上,垃圾收集器会成为问题吗?
因此,我以 ILSpy'ing .NET 程序集的形式进行了一些研究,并在 .NET Core 2.1 上进行了一些测试。
我的测试结果如下:
interface ITest
{
Span<byte> Data { get; }
}
unsafe struct TestStruct : ITest
{
fixed byte dataField[8];
public Span<byte> Data
{
get
{
//Unsafe.AsPointer() to avoid the fixed expression :-)
return new Span<byte>(Unsafe.AsPointer(ref dataField[0]), 8);
}
}
}
class Program
{
//Note: This test is done in Debug mode to make sure the string allocation isn't ommited
static void Main(string[] args)
{
new string('c', 200);
//Boxes the struct onto the heap.
//The object is allocated after the string to ensure it will be moved during GC compacting
ITest HeapAlloc = new TestStruct();
Span<byte> span1, span2;
span1 = HeapAlloc.Data; //Creates span to old location
GC.Collect(2, GCCollectionMode.Forced, true, true); //Force a compacting garbage collection
span2 = HeapAlloc.Data; //Creates span to new location
//Ensures that if the pointer to span1 wasn't updated, that there wouldn't be heap corruption
//Write to Span2
span2[0] = 5;
//Read from Span1
Console.WriteLine(span1[0] == 5); //Prints true in .NET Core 2.1, span1's pointer is updated
}
}
我从对 IL 的研究中学到的东西,如果我没有正确解释这一点,请原谅我:
.NET Core 的 2 个字段跨度:
//Note, this is not the complete declaration, just the fields
public ref readonly struct Span<T>
{
internal readonly ByReference<T> _pointer;
private readonly int _length;
}
.NET Framework 的 3 个字段跨度:
//Same note as 2 Field Span
public ref readonly struct Span<T>
{
private readonly Pinnable<T> _pinnable;
private readonly IntPtr _byteOffset;
private readonly int _length;
}
.Net Core使用的是Span的2字段模型。由于 .NET Framework 使用 3 字段模型,它的指针将不会更新。原因? 3 个字段跨度的 Span<T>(void* pointer, int length)
构造函数(我为此使用)将 _byteOffset
字段设置为 pointer
参数。 GC 更新的 3 个字段范围内的指针是 _pinnable
字段。对于 2 字段 Span,它们是相同的。
所以,我的问题的答案是,是的,我可以使用或不使用固定语句将 Span 指向固定缓冲区,但是在不使用 .NET Core 的 2 字段 Span 模型时这样做是很危险的.如果我对 .NET Framework 的当前 Span 模型有误,请纠正我。
我使用的是 .NET Core 2.1 和语言标准 7.3。我希望在不获取指向它的指针的情况下引用固定缓冲区。目前可以吗?
public unsafe struct InteropStruct
{
private fixed byte dataField[32];
public Span<byte> Data
{
get
{
//return a span referencing the private field without a fixed statement
}
}
}
我知道 Span 目前能够通过垃圾回收来跟踪托管数组,所以我没有看到任何东西阻止它以类似的方式跟踪固定缓冲区。
如果不可能,如果我确实使用这样的固定语句会发生什么:
public unsafe struct InteropStruct
{
private fixed byte dataField[32];
public Span<byte> Data
{
get
{
fixed (byte* ptr = dataField)
{
return new Span<byte>(ptr, 32);
}
}
}
}
如果结构被包装在一个对象中或 class 在堆上,垃圾收集器会成为问题吗?
因此,我以 ILSpy'ing .NET 程序集的形式进行了一些研究,并在 .NET Core 2.1 上进行了一些测试。 我的测试结果如下:
interface ITest
{
Span<byte> Data { get; }
}
unsafe struct TestStruct : ITest
{
fixed byte dataField[8];
public Span<byte> Data
{
get
{
//Unsafe.AsPointer() to avoid the fixed expression :-)
return new Span<byte>(Unsafe.AsPointer(ref dataField[0]), 8);
}
}
}
class Program
{
//Note: This test is done in Debug mode to make sure the string allocation isn't ommited
static void Main(string[] args)
{
new string('c', 200);
//Boxes the struct onto the heap.
//The object is allocated after the string to ensure it will be moved during GC compacting
ITest HeapAlloc = new TestStruct();
Span<byte> span1, span2;
span1 = HeapAlloc.Data; //Creates span to old location
GC.Collect(2, GCCollectionMode.Forced, true, true); //Force a compacting garbage collection
span2 = HeapAlloc.Data; //Creates span to new location
//Ensures that if the pointer to span1 wasn't updated, that there wouldn't be heap corruption
//Write to Span2
span2[0] = 5;
//Read from Span1
Console.WriteLine(span1[0] == 5); //Prints true in .NET Core 2.1, span1's pointer is updated
}
}
我从对 IL 的研究中学到的东西,如果我没有正确解释这一点,请原谅我:
.NET Core 的 2 个字段跨度:
//Note, this is not the complete declaration, just the fields
public ref readonly struct Span<T>
{
internal readonly ByReference<T> _pointer;
private readonly int _length;
}
.NET Framework 的 3 个字段跨度:
//Same note as 2 Field Span
public ref readonly struct Span<T>
{
private readonly Pinnable<T> _pinnable;
private readonly IntPtr _byteOffset;
private readonly int _length;
}
.Net Core使用的是Span的2字段模型。由于 .NET Framework 使用 3 字段模型,它的指针将不会更新。原因? 3 个字段跨度的 Span<T>(void* pointer, int length)
构造函数(我为此使用)将 _byteOffset
字段设置为 pointer
参数。 GC 更新的 3 个字段范围内的指针是 _pinnable
字段。对于 2 字段 Span,它们是相同的。
所以,我的问题的答案是,是的,我可以使用或不使用固定语句将 Span 指向固定缓冲区,但是在不使用 .NET Core 的 2 字段 Span 模型时这样做是很危险的.如果我对 .NET Framework 的当前 Span 模型有误,请纠正我。