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



我有一个字节数组,它以小字节序从函数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'      
}
}

这里有趣的步骤是以小端序的方式可靠地读取字节(这里的"可靠"意味着"适用于任何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,或者如果您使用其他 nuget。如果这可能是一个问题(或者您针对较旧的框架版本(,您可以使用这样的解决方案:

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;
}

试试这个:

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();
}

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

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值无效。

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

最新更新