C# 获得Span<;字节>;在不复制结构的情况下覆盖结构

C# 获得Span<;字节>;在不复制结构的情况下覆盖结构,c#,system.io.pipelines,C#,System.io.pipelines,我一直在试验Span作为ReadOnlySequence和System.IO.pipeline的一部分 我目前正在尝试通过struct获取Span,而不使用unsafe代码,也不复制该struct 我的结构很简单: [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode)] public struct Packet { public byte TestByte;

我一直在试验
Span
作为
ReadOnlySequence
和System.IO.pipeline的一部分

我目前正在尝试通过
struct
获取
Span
,而不使用
unsafe
代码,也不复制该
struct

我的结构很简单:

    [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode)]
    public struct Packet
    {
        public byte TestByte;
    }
方法1——有效——但感觉“不安全”

但是我所拥有的ToByteArray()的任何当前实现仍在制作数据包结构的副本

我不能做像这样的事情:

Span<byte> packetSpan = (byte[])packet;
    // ^^ Won't compile
Span packetSpan=(字节[])数据包;
//^^无法编译

如果没有
不安全
,就无法在任意结构上获取
Span
,因为这样的Span将允许您以任何方式更改结构的任何位,可能会违反类型的不变量-这本质上是一个不安全的操作

好的,但是如何处理
ReadOnlySpan
?请注意,您必须将
StructLayoutAttribute
放在您的结构上,这样代码才有意义。这应该是一个暗示。想象一下,尝试编写一个更简单的方法,该方法为任意
T(其中T:struct
)返回一个
byte[]
。你必须先找出结构的大小,不是吗?那么,如何计算C#中结构的大小呢?您可以使用
sizeof
操作符,该操作符需要
unsafe
上下文,并且需要将结构作为;或者,您也可以选择不可靠的、仅适用于具有顺序或显式字节布局的结构没有安全、通用的方法,因此您不能这样做

Span
ReadOnlySpan
在设计时并没有考虑访问结构字节,而是考虑了数组的跨段,这些数组具有已知的大小并保证是顺序的

如果你确信自己知道自己在做什么,那么你可以在
不安全的
环境中这样做——这就是它的目的。但是请注意,基于上述原因,您使用
不安全
的解决方案不会推广到任意结构

如果您打算将结构用作IO操作的缓冲区,您可能需要研究一下。它们还需要
不安全的
上下文,但您可以将不安全性封装在结构中,并将
Span
返回到该固定缓冲区。基本上,任何处理内存中对象字节结构的操作都需要.NET中的
不安全
,因为内存管理就是这个“安全”所指的东西。

你必须以安全的方式进行,因为从这个词的真正含义来看,它是不安全的,因为如果你不够小心,你会朝自己的脚开枪。原因如下:

考虑以下代码:

Span<byte> GiveMeSpan() 
{
    MyLovelyStruct value = new MyLovelyStruct();
    unsafe 
    {
        return new Span<byte>(&value, sizeof(MyLovelyStruct));
    }
}
Process()
方法正在处理您的
mylovelstruct
mylovelclass
在内存中突然移动时(是的,内存中GC move的对象,)会发生一个错误?是的,指向
mylovelstruct
Span
将不再指向新的
mylovelstruct
地址,您的程序将损坏

因此,为了使用
Span
或任何其他指针类型安全地包装
struct
,您必须确保:

  • 实例位于固定的内存位置(例如,在堆栈或非托管内存中,如由分配的内存块)
  • 在完成指针操作之前,不会声明实例内存

因此,需要
不安全的
关键字,即使您可以绕过它,也要由您向代码的读者发出警告。

我认为您可以编写用户定义的转换运算符。我很确定,如果没有
不安全
,一般来说,对于一个结构来说,没有办法做到这一点,因为如果你在一个结构的所有字节上获得一个
Span
,你就有可能以任何方式改变该结构中的任何位,这本身就是不安全的。这两个都是完美的答案。谢谢你的努力,我真的很感谢你的努力,我需要选择一个作为答案,尽管我想选择两者。所以我担心这是我的RNG的一个例子。这两个都是完美的答案。谢谢你的努力,我真的很感激你的努力,还有那些指向固定大小缓冲区的指针。我需要选择一个作为答案,虽然我想选择两者。因此,我担心这是我的RNG的一个案例。
Span<byte> packetSpan = new Packet().ToByteArray();
Span<byte> packetSpan = (byte[])packet;
    // ^^ Won't compile
Span<byte> GiveMeSpan() 
{
    MyLovelyStruct value = new MyLovelyStruct();
    unsafe 
    {
        return new Span<byte>(&value, sizeof(MyLovelyStruct));
    }
}
class MyLovelyClass 
{
    private MyLovelyStruct value;

    public void Foo() 
    {
        unsafe 
        {
            var span = new Span(&value, sizeof(MyLovelyStruct));
            Process(span);
        }
    }
}

// Declaration 
Process(Span<byte> span);