c# 将字节数组转换为结构数组

问题描述 投票:0回答:2

我在 C# 中有这段代码,我需要以最快的方式将字节数组复制到结构数组。

我有这样的结构:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe public struct TsVarGroup
{
    public uint Addr;       
    public ushort Count;    
    public short CAddr;     
    public ushort CSW;      
    public fixed byte NamePrefix[9];

    public string GetNamePrefix()
    {
        fixed (byte* ptr = NamePrefix)
        {
            return new string((sbyte*)ptr, 0, 8, Encoding.ASCII);
        }
    }
}
asVarGroups = new TsVarGroup[sVarTable.Count];
byte[] dataVarGroups = Connection.GETMEM("R", (sVarTable.Addr - ADDRESS_OFFSET), asVarGroups.Length * TS_VARGROUP_SIZE);

有没有简单的方法可以将字节数组

dataVarGroups
复制到结构数组
asVarGroups

我试过这个:

Buffer.BlockCopy(dataVarGroups, 0, asVarGroups, 0, dataVarGroups.Length); 

但它仅适用于基元元素。

c# arrays casting copy
2个回答
1
投票

您可以使用

MemoryMarshal.Cast
将一种 blittable 类型的跨度强制转换为另一种类型;数组可以被视为跨度,并且字节和您的类型似乎都是 blittable,所以:

byte[] bytes = ...
TsVarGroup[] target = ...

MemoryMarshal.Cast<byte, TsVarGroup>(bytes).CopyTo(target);

您可以这样做以在任何方向上强制 blittable 类型。生成的跨度将根据被双关的两种类型各自的大小正确调整大小 - 即,如果您将 100 字节的跨度强制为

int
,它将具有长度 25。

这与强制非托管指针类似,但不需要固定或

unsafe
代码(尽管显然:您仍然需要小心)。

最常见的是,这种强制转换用于避免需要第二个数组,从而允许您处理一种数组类型就像它是另一种数组(通过跨度)。


0
投票

如果您使用的是 C# 12 或更高版本,则无需使用任何

unsafe
代码即可通过使用新的
System.Runtime.CompilerServices.InlineArray
属性来定义 9 字节的固定数组来执行此操作,如下所示:

[System.Runtime.CompilerServices.InlineArray(9)]
public struct NineBytes
{
    byte _element0;

    public static NineBytes Create(IReadOnlyList<byte> bytes)
    {
        var result = new NineBytes();

        for (int i = 0; i < 9; ++i)
            result[i] = bytes[i];

        return result;
    }
}

我为下面的演示程序添加了一个

Create()
辅助方法到该结构;它不是结构的必要部分。

一旦你有了它,你就可以像这样声明你的结构:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct TsVarGroup
{
    public uint      Addr;
    public ushort    Count;
    public short     CAddr;
    public ushort    CSW;
    public NineBytes NamePrefix;

    public string GetNamePrefix()
    {
        return Encoding.ASCII.GetString(NamePrefix);
    }
}

然后您可以使用

MemoryMarshal.Cast<>
将字节数组转换(不复制数据)到
Span<TsVarGroup>
ReadOnlySpan<TsVarGroup>

Span<TsVarGroup> fromBytes = MemoryMarshal.Cast<byte, TsVarGroup>(asBytes);

将所有内容放在一个简单的控制台测试应用程序中:

using System.Runtime.InteropServices;
using System.Text;

namespace Net8Console;

public static class Program
{
    public static void Main()
    {
        // Create array of structs:

        TsVarGroup[] original = [
            new TsVarGroup
            {
                Addr       = 1,
                Count      = 2,
                CAddr      = 3,
                CSW        = 4,
                NamePrefix = createNineAsciiBytes('A')
            },
            new TsVarGroup
            {
                Addr       = 2,
                Count      = 3,
                CAddr      = 4,
                CSW        = 5,
                NamePrefix = createNineAsciiBytes('B')
            },
            new TsVarGroup
            {
                Addr       = 3,
                Count      = 4,
                CAddr      = 5,
                CSW        = 6,
                NamePrefix = createNineAsciiBytes('C')
            }
        ];

        byte[]           asBytes   = MemoryMarshal.AsBytes(original.AsSpan()).ToArray();
        Span<TsVarGroup> fromBytes = MemoryMarshal.Cast<byte, TsVarGroup>(asBytes);

        // No copying of the 'asBytes' array occurs when creating 'fromBytes'.

        for (int i = 0; i < fromBytes.Length; i++)
        {
            Console.WriteLine(fromBytes[i].GetNamePrefix());
        }
    }

    static NineBytes createNineAsciiBytes(char firstChar)
    {
        return NineBytes.Create(nineAsciiChars((byte)firstChar).ToList());
    }

    static IEnumerable<byte> nineAsciiChars(byte firstChar)
    {
        for (byte ch = firstChar, lastChar = (byte)(ch + 9); ch != lastChar; ++ch)
            yield return ch;
    }
}

[System.Runtime.CompilerServices.InlineArray(9)]
public struct NineBytes
{
    byte _element0;

    public static NineBytes Create(IReadOnlyList<byte> bytes)
    {
        var result = new NineBytes();

        for (int i = 0; i < 9; ++i)
            result[i] = bytes[i];

        return result;
    }
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct TsVarGroup
{
    public uint      Addr;
    public ushort    Count;
    public short     CAddr;
    public ushort    CSW;
    public NineBytes NamePrefix;

    public string GetNamePrefix()
    {
        return Encoding.ASCII.GetString(NamePrefix);
    }
}

如果你运行它,它将输出:

ABCDEFGHI
BCDEFGHIJ
CDEFGHIJK

这表明数据已从原始数据正确转换。

© www.soinside.com 2019 - 2024. All rights reserved.