繁体   English   中英

将字节数组转换为 C# 中的枚举集合

[英]Convert byte array to collection of enums in C#

我有一个字节数组,它从 function GetData()以小字节序字节顺序接收枚举,我想将该数组转换为枚举集合。 我如何将 LE 顺序的字节复制并转换为 C# 中的枚举值? 我有 C++ 背景并且不太熟悉该语言。 这是一个示例代码片段:

public enum BarID
{
  TAG0 = 0x0B01,
  TAG1 = 0x0B02,
}

public class TestClass
{
  List<BarID> ids;
  internal TestClass() 
  {
      ids = new List<BarID>();
      byte[] foo = GetData(); // returns  01 0b 00 00 02 0b 00 00
      // cast byte array so that ids contains the enums 'TAG0' and 'TAG1'      

  }
}

试试这个:

var foo = new byte[] {0x01, 0x0b, 0x00, 0x00, 0x02, 0x0b, 0x00, 0x00}.ToList();
IEnumerable<byte> bytes;
var result = new List<BarID>();

while ((bytes = foo.Take(4)).Any())
{
    var number = BitConverter.IsLittleEndian
        ? BitConverter.ToInt32(bytes.ToArray(), 0)
        : BitConverter.ToInt32(bytes.Reverse().ToArray(), 0);


    var enumValue = (BarID) number;

    result.Add(enumValue);

    foo = foo.Skip(4).ToList();
}

这里有趣的一步是以小端方式可靠地读取字节(这里的“可靠”意味着“可以在任何 CPU 上工作,而不仅仅是恰好是小端本身的 CPU”); 幸运的是, BinaryPrimitives使这一点变得显而易见,为您提供了来自Span<byte>int (来自GetData()byte[]可隐式转换为Span<byte> )。 然后你可以从int转换为BarID

Span<byte> foo = GetData();
var result = new BarID[foo.Length / 4];
for (int i = 0; i < result.Length; i++)
{
    result[i] = (BarID)BinaryPrimitives.ReadInt32LittleEndian(foo.Slice(4 * i));
}

这里的Slice步骤只是偏移了我们应该在跨度中开始读取的位置。

尽管 Marc 的回答既好又快,但它仅适用于 .NET Core,或者如果您使用其他 nugets。 如果这可能是个问题(或者你的目标是旧的框架版本),你可以使用这样的解决方案:

var bytes = new byte[] { 0x01, 0x0b, 0x00, 0x00, 0x02, 0x0b, 0x00, 0x00 };

return BitConverter.IsLittleEndian
    ? ConvertLittleEndian(bytes)
    : ConvertBigEndian(bytes);

其中转换方法:

private static unsafe BarID[] ConvertLittleEndian(byte[] bytes)
{
    var barIds = new BarID[bytes.Length / 4];
    fixed (byte* pBytes = bytes)
    {
        BarID* asIds = (BarID*)pBytes;
        for (int i = 0; i < barIds.Length; i++)
            barIds[i] = asIds[i];
    }

    return barIds;
}

如果您知道您的代码将在小端 CPU 上使用(例如,它应该是一个 Windows 应用程序),那么您甚至不需要大端版本:

private static BarID[] ConvertBigEndian(byte[] bytes)
{
    var barIds = new BarID[bytes.Length / 4];
    for (int i = 0; i < barIds.Length; i++)
    {
        int offset = i * 4;
        barIds[i] = (BarID)((bytes[offset] << 3) | (bytes[offset + 1] << 2)
            | (bytes[offset + 2] << 1) | bytes[offset + 3]);
    }

    return barIds;
}

目前尚不清楚数组值是按两个还是四个分组,但模式基本相同:

public enum BarID
{
    TAG0 = 0x0B01,
    TAG1 = 0x0B02,
}

public class TestClass
{
    List<BarID> ids;
    internal TestClass()
    {
        ids = new List<BarID>();
        byte[] foo = GetData(); // returns  01 0b 00 00 02 0b 00 00
                                // cast byte array so that ids contains the enums 'TAG0' and 'TAG1'      

        //create a hash-set with all the enum-valid values
        var set = new HashSet<int>(
            Enum.GetValues(typeof(BarID)).OfType<int>()
            );

        //scan the array by 2-bytes
        for (int i = 0; i < foo.Length; i += 2)
        {
            int value = foo[i] + foo[i + 1] << 8;
            if (set.Contains(value))
            {
                ids.Add((BarID)value);
            }
        }
    }
}

该集合不是强制性的,但它应该防止将无效值强制转换为标记。 例如,如果值是基于单词的,则00 00值无效。

作为最终考虑,我不会在 class 构造函数中执行类似任务:最好创建实例,然后调用专用方法进行转换。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM