首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >C#可以用一个结构覆盖一个字节缓冲区吗?

C#可以用一个结构覆盖一个字节缓冲区吗?
EN

Stack Overflow用户
提问于 2022-05-25 20:32:40
回答 1查看 357关注 0票数 0

我有一个非托管C/C++库,用于解码可变大小的字节缓冲区。这些字节构成一个从SQLite BLOB中提取的复杂数据报。在C/C++代码中,很容易使用指针在数据的基础上覆盖一个结构。在C#中还有同样的事情要做吗?通过使用Marshal类,我将向Byte[]传递一个引用到非托管端。我还可以传递对托管端结构的引用,这些结构将被填充。我想把这些都拉到C#那里去。有什么想法吗有人吗?

蒂娅,道格

EN

回答 1

Stack Overflow用户

发布于 2022-05-26 11:28:50

这个答案假设了以下几点:

  1. 您的结构只包含基本值类型(即不包含数组、不包含引用类型、不包含decimal值、不包含string等)。
  2. 您有一个字节数组,在各种偏移量处包含一个或多个结构。
  3. 您正在使用.Net Core3.1或更高版本。
  4. 您正在使用C# 7.0或更高版本。

可以在字节数组的一个部分上“覆盖”一个结构,这样您就可以通过结构访问数组中的数据,而无需对数据进行额外的复制。为此,您可以使用SpanMemoryMarshal.AsRef

当然,在这样做的时候,结构填料很重要。您必须指定结构的打包,以匹配字节数组中的打包。这与声明用于P/Invoke的结构时所做的完全相同。

(尽管有些人可能声称,StructLayoutAttribute.Pack设置确实会影响内存中结构的布局;它不仅仅是针对P/Invoke的。)

如果在结构中使用char值,还必须将CharSet指定为Unicode。如果源结构没有对字符使用UTF16 (如果它们使用ANSI或ASCII),则需要将这些字段声明为byte而不是char,因为C#字符总是UTF16。

一旦您正确地布局了您的结构,并且您有一个包含一个或多个结构的byte[]数组,您就可以在数组的一个部分上覆盖一个结构,如下所示:

  1. 使用[Span<byte>(byte[] array, int start, int length)](https://learn.microsoft.com/en-us/dotnet/api/system.span-1.-ctor?view=net-6.0#system-span-1-ctor(-0(%29-system-int32-system-int32%29)构造函数创建一个https://learn.microsoft.com/en-us/dotnet/api/system.span-1.-ctor?view=net-6.0#system-span-1-ctor(-0(%29-system-int32-system-int32%29,以引用数组的子集,该子集对应于结构的字节。
  2. 使用MemoryMarshal.AsRef引用字节数组中的结构。这直接引用字节数组中的结构,而不复制任何数据。

下面的程序演示了这种方法。

它定义了三个不同的结构,Demo1Demo2Demo3,每个结构都有不同的包装。

它实现了一个toByteArray()方法,其唯一目的是将结构转换为一个字节数组,在开始和结束时有一些备用字节。实现这一点并不重要;您可以从P/Invoke中获取字节数组。

它还实现了一个助手方法AsSpan()。这是一个非常有用的方法;它将任何闪存(非托管)结构转换为Span<byte>()

有趣的方法是consumeStructs()。这将在给定的byte[]数组上覆盖三个不同的结构,然后打印出它们的内容。

如果运行此程序,您将看到输出与Main()中的结构初始化匹配。

注意,这些结构被声明为ref var。这意味着没有复制数据;结构实际上是直接引用传递给方法的data[]数组中的数据。

我通过修改data[]数组中对应于Demo2结构开头的一个字节来演示这种情况,如下所示:

代码语言:javascript
运行
复制
data[prefixByteCount + Marshal.SizeOf<Demo1>()] = (byte)'*';

在进行更改并重新打印结构后,输出显示CharValue已从0更改为*。这说明结构确实直接在data[]数组中引用数据。

下面是可编译的控制台应用程序:

(在网上试试)

代码语言:javascript
运行
复制
using System;
using System.Linq;
using System.Runtime.InteropServices;

namespace Demo
{
    static class Program
    {
        static void Main()
        {
            var demo1 = new Demo1
            {
                BoolValue   = true,
                DoubleValue = -1
            };

            var demo2 = new Demo2
            {
                CharValue   = '0',
                ShortValue  = 1,
                IntValue    = 2,
                LongValue   = 3,
                FloatValue  = 4,
                DoubleValue = 5.4321
            };

            var demo3 = new Demo3
            {
                ByteValue  = 128,
                FloatValue = 1.23f
            };

            const int PREFIX_BYTE_COUNT  = 29;
            const int POSTFIX_BYTE_COUNT = 17;

            var bytes = toByteArray(PREFIX_BYTE_COUNT, POSTFIX_BYTE_COUNT, ref demo1, ref demo2, ref demo3);

            consumeStructs(PREFIX_BYTE_COUNT, bytes);
        }

        static byte[] toByteArray(int prefixByteCount, int postfixByteCount, ref Demo1 demo1, ref Demo2 demo2, ref Demo3 demo3)
        {
            var demo1Bytes = AsSpan(ref demo1);
            var demo2Bytes = AsSpan(ref demo2);
            var demo3Bytes = AsSpan(ref demo3);

            var prefixBytes  = Enumerable.Repeat((byte)0, prefixByteCount);
            var postfixBytes = Enumerable.Repeat((byte)0, postfixByteCount);

            return prefixBytes
               .Concat(demo1Bytes.ToArray())
               .Concat(demo2Bytes.ToArray())
               .Concat(demo3Bytes.ToArray())
               .Concat(postfixBytes)
               .ToArray();
        }

        static void consumeStructs(int prefixByteCount, byte[] data)
        {
            var demo1Bytes = new Span<byte>(data, prefixByteCount,                                                     Marshal.SizeOf<Demo1>());
            var demo2Bytes = new Span<byte>(data, prefixByteCount + Marshal.SizeOf<Demo1>(),                           Marshal.SizeOf<Demo2>());
            var demo3Bytes = new Span<byte>(data, prefixByteCount + Marshal.SizeOf<Demo1>() + Marshal.SizeOf<Demo2>(), Marshal.SizeOf<Demo3>());

            ref var demo1Ref = ref MemoryMarshal.AsRef<Demo1>(demo1Bytes);
            ref var demo2Ref = ref MemoryMarshal.AsRef<Demo2>(demo2Bytes);
            ref var demo3Ref = ref MemoryMarshal.AsRef<Demo3>(demo3Bytes);

            Console.WriteLine(demo1Ref);
            Console.WriteLine(demo2Ref);
            Console.WriteLine(demo3Ref);

            Console.WriteLine("Modifying first byte of Demo2 struct in byte buffer.");

            data[prefixByteCount + Marshal.SizeOf<Demo1>()] = (byte)'*';
            Console.WriteLine(demo2Ref);
        }

        public static Span<byte> AsSpan<T>(ref T val) where T : unmanaged
        {
            var valSpan = MemoryMarshal.CreateSpan(ref val, 1);
            return MemoryMarshal.Cast<T, byte>(valSpan);
        }
    }

    [StructLayout(LayoutKind.Sequential, Pack = 8)]
    public struct Demo1
    {
        public bool   BoolValue;
        public double DoubleValue;

        public override string ToString()
        {
            return $"byte = {BoolValue}, double = {DoubleValue}";
        }
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode)]
    public struct Demo2
    {
        public char   CharValue;
        public short  ShortValue;
        public int    IntValue;
        public long   LongValue;
        public float  FloatValue;
        public double DoubleValue;

        public override string ToString()
        {
            return $"char = {CharValue}, short = {ShortValue}, int = {IntValue}, long = {LongValue}, float = {FloatValue}, double = {DoubleValue}";
        }
    }

    [StructLayout(LayoutKind.Sequential, Pack = 2)]
    public struct Demo3
    {
        public byte  ByteValue;
        public float FloatValue;

        public override string ToString()
        {
            return $"byte = {ByteValue}, float = {FloatValue}";
        }
    }
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72383841

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档