【发布时间】:2020-04-23 01:37:44
【问题描述】:
我一直在尝试将 Span<T> 作为 ReadOnlySequence<T> 和 System.IO.Pipelines 的一部分。
我目前正在尝试在不使用 unsafe 代码且不复制 struct 的情况下通过 struct 获取 Span<T>。
我的结构很简单:
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode)]
public struct Packet
{
public byte TestByte;
}
方法 1 - 可行 - 但感觉“不安全”
//
// Method 1 - uses Unsafe to get a span over the struct
//
var packet = new Packet();
unsafe
{
var packetSpan = new Span<byte>(&packet, Marshal.SizeOf(packet));
packetSpan[0] = 0xFF; // Set the test byte
Debug.Assert(packet.TestByte == 0xFF, "Error, packetSpan did not update packet.");
// ^^^ Succeeds
packet.TestByte = 0xEE;
Debug.Assert(packetSpan[0] == 0xEE, "Error, packet did not update packetSpan.");
// ^^^ Succeeds
}
方法 2 - 不能按预期工作,因为它需要副本
//
// Method 2
//
// This doesn't work as intended because the original packet is actually
// coppied to packet2Array because it's a value type
//
// Coppies the packet to an Array of Packets
// Gets a Span<Packet> of the Array of Packets
// Casts the Span<Packet> as a Span<byte>
//
var packet2 = new Packet();
// create an array and store a copy of packet2 in it
Packet[] packet2Array = new Packet[1];
packet2Array[0] = packet2;
// Get a Span<Packet> of the packet2Array
Span<Packet> packet2SpanPacket = MemoryExtensions.AsSpan<Packet>(packet2Array);
// Cast the Span<Packet> as a Span<byte>
Span<byte> packet2Span = MemoryMarshal.Cast<Packet, byte>(packet2SpanPacket);
packet2Span[0] = 0xFF; // Set the test byte
Debug.Assert(packet2.TestByte == 0xFF, "Error, packet2Span did not update packet2");
// ^^^ fails because packet2 was coppied into the array, and thus packet2 has not changed.
Debug.Assert(packet2Array[0].TestByte == 0xFF, "Error, packet2Span did not update packet2Array[i]");
// ^^^ succeeds
packet2.TestByte = 0xEE;
Debug.Assert(packet2Span[0] == 0xEE, "Error, packet2 did not update in packet2Span");
// ^^^ fails because packet2Span is covering packet2Array which has a copy of packet2
packet2Array[0].TestByte = 0xEE;
Debug.Assert(packet2Span[0] == 0xEE, "Error, packet2 did not update in packet2Span");
// ^^^ succeeds
进一步的研究表明
Span<T> 可以从 byte[] 隐式转换,例如,我可以这样做
Span<byte> packetSpan = new Packet().ToByteArray();
但我目前的任何 ToByteArray() 实现仍在制作 Packet 结构的副本。
我不能这样做:
Span<byte> packetSpan = (byte[])packet;
// ^^ Won't compile
【问题讨论】:
-
我认为您可以编写用户定义的转换运算符。 docs.microsoft.com/en-us/dotnet/csharp/language-reference/…
-
我很确定如果没有
unsafe,一般的结构就无法做到这一点,因为如果你在结构的所有字节上获得Span,你可能会改变任何位以任何方式在该结构中 - 这本质上是不安全的。