将字节数组转换为 C# 中的枚举集合
Convert byte array to collection of enums in 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'
}
}
不清楚数组值是按两个还是四个分组,但模式基本相同:
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 构造函数中执行类似任务:最好创建实例,然后调用专用方法进行转换。
试试这个:
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();
}
这里有趣的一步是以小端方式读取字节 reliably(这里的 "reliable" 表示 "works on any CPU, not just one that happens to be little-endian itself");幸运的是,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。如果这可能是个问题(或者您的目标是较旧的 Framework 版本),您可以使用这样的解决方案:
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;
}
我有一个字节数组,它从函数 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'
}
}
不清楚数组值是按两个还是四个分组,但模式基本相同:
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 构造函数中执行类似任务:最好创建实例,然后调用专用方法进行转换。
试试这个:
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();
}
这里有趣的一步是以小端方式读取字节 reliably(这里的 "reliable" 表示 "works on any CPU, not just one that happens to be little-endian itself");幸运的是,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。如果这可能是个问题(或者您的目标是较旧的 Framework 版本),您可以使用这样的解决方案:
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;
}