如何在C#中将值类型转换为byte[]?

10
我希望做到与此相当的事情:
byte[] byteArray;
enum commands : byte {one, two};
commands content = one;
byteArray = (byte*)&content;

是的,现在它是一个字节,但考虑到未来我想要更改它,我该怎么办?如何使byteArray包含内容?(我不想复制它。)

5个回答

21

将任何值类型(不仅限于基本类型)转换为字节数组,反之亦然:

    public T FromByteArray<T>(byte[] rawValue)
    {
        GCHandle handle = GCHandle.Alloc(rawValue, GCHandleType.Pinned);
        T structure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
        handle.Free();
        return structure;
    }

    public byte[] ToByteArray(object value, int maxLength)
    {
        int rawsize = Marshal.SizeOf(value);
        byte[] rawdata = new byte[rawsize];
        GCHandle handle =
            GCHandle.Alloc(rawdata,
            GCHandleType.Pinned);
        Marshal.StructureToPtr(value,
            handle.AddrOfPinnedObject(),
            false);
        handle.Free();
        if (maxLength < rawdata.Length) {
            byte[] temp = new byte[maxLength];
            Array.Copy(rawdata, temp, maxLength);
            return temp;
        } else {
            return rawdata;
        }
    }

非常好,正是我一直在寻找的,这应该是正确的答案,因为BitConverter并不适用于所有类型。 - Qwerty01
2
BitConverter在所有支持它的平台上对所有支持的类型都是一致的。但需要注意的是,从此方法返回的字节在不同平台之间是不可移植的,甚至可能在不同版本的.NET框架之间无法工作,因此除非您可以保证序列化和反序列化端的.NET框架版本相同,否则它不适用于序列化。这可能会使升级应用程序变得非常困难。 - Mike Marynowski
1
@MikeMarynowski:刚看到你的评论。我不知道你的意思。这段代码并没有涉及二进制序列化,它使用了封送,而封送的目的是为了获得你请求调用P/Invoke或COM API所需的确切字节。通过这种方法,无论是.NET版本还是平台,你都可以获得你在结构体中定义的确切字节(使用StructLayoutAttribute等), - OregonGhost
@OregonGhost,优美的解决方案,但我想知道如果没有使用StructLayoutAttributes定义结构体,那么是什么决定了输出字节的顺序?它不能在不同版本的.NET中发生变化吗?也许这就是Mike所说的。 - Sergey Nefedyev
@CRG:也许吧。但是我从来不会考虑使用未经StructLayoutAttribute正确声明的结构体进行编组。对于基本结构体(即由BitConverter支持的类型),这也永远不会改变。BitConverter在不同平台上的表现也不一致,它取决于系统的字节序,就像编组一样。这就是为什么您通常会为处理字节序的整数字段添加属性,而实际字段已经处于网络顺序的原因。 - OregonGhost
@OregonGhost 啊,我刚注意到你的回复。我的意思是框架值类型的内部结构布局可能会在版本之间发生变化,除非结构布局是明确的,否则不同的JIT编译器可以以不同的方式优化结构布局,例如基于系统字大小。但我错了,关于BitConverter在各平台上具有一致性-实际上它并没有标准化字节顺序,正如你所指出的那样。这就是为什么我们在意识到后很快改变成BinaryReader/BinaryWriter的原因,因为它们在所有平台上都是一致的。 - Mike Marynowski

16

你可能需要使用BitConverter类。例如:

int input = 123;
byte[] output = BitConverter.GetBytes(input);
如果你的枚举类型已知为Int32派生类型,那么你可以先将其值进行强制转换:
BitConverter.GetBytes((int)commands.one);

如果原始类型不足够,可以查看我的答案了解如何转换任何值类型。 - OregonGhost

5

对于任何想了解如何在不使用 BitConverter 的情况下完成它的人,您可以像这样做:

// Convert double to byte[]
public unsafe byte[] pack(double d) {
    byte[] packed = new byte[8]; // There are 8 bytes in a double
    void* ptr = &d; // Get a reference to the memory containing the double
    for (int i = 0; i < 8; i++) { // Each one of the 8 bytes needs to be added to the byte array
        packed[i] = (byte)(*(UInt64 *)ptr >> (8 * i)); // Bit shift so that each chunk of 8 bits (1 byte) is cast as a byte and added to array 
    }
    return packed;
}

// Convert byte[] to double
public unsafe double unpackDouble(byte[] data) {
    double unpacked = 0.0; // Prepare a chunk of memory ready for the double
    void* ptr = &unpacked; // Reference the double memory
    for (int i = 0; i < data.Length; i++) {
        *(UInt64 *)ptr |= ((UInt64)data[i] << (8 * i)); // Get the bits into the right place and OR into the double
    }
    return unpacked;
}

实际上,使用 BitConverter 更加容易和安全,但了解这个也很有趣!


1
这种方法在尝试编写低分配代码时,随着引入 Spans 变得更加相关...我自己正在探索这个问题,想知道通过 ((*tvalue)bytePointer)[0] = value 进行写入是否存在任何问题(当然,假设大小已经被检查过了)。 - tobriand

3

1

如果您在意性能且不想干扰垃圾回收机制,下面是一种通用且不安全的变体:

        static unsafe void StructToByteArr<T>(ref T obj, Span<byte> dest) where T : unmanaged
        {
            fixed (void* pSrc = &obj, pDest = dest)
            {
                Buffer.MemoryCopy(pSrc, pDest, dest.Length, sizeof(T));
            }
        }
        static unsafe void StructToByteArr<T>(ref T obj, byte[] dest, int destOffset) where T : unmanaged
        {
            StructToByteArr(ref obj, new Span<byte>(dest, destOffset, sizeof(T)));
        }

        static unsafe byte[] StructToByteArr<T>(ref T obj) where T : unmanaged
        {
            var result = new byte[sizeof(T)];
            StructToByteArr(ref obj, result, 0);
            return result;
        }

如果传递的结构体比指针大小小,则可以删除ref

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接