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

C# 在C语言中将字节数组转换为枚举集合,c#,casting,endianness,C#,Casting,Endianness,我有一个字节数组,它从函数GetData以小字节顺序接收枚举,我想将数组转换为枚举的集合。 如何将字节按LE顺序复制并强制转换为C中的枚举值?我有C++背景,对语言不太熟悉。 这是一个示例代码段: public enum BarID { TAG0 = 0x0B01, TAG1 = 0x0B02, } public class TestClass { List<BarID> ids; internal TestClass() { ids = new

我有一个字节数组,它从函数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值无效


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

不清楚数组值是否按两个或四个分组,但是模式基本相同:

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

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

尝试以下操作:

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();
}
试试这个:

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中的int,GetData中的byte[]可以隐式地转换为Span。然后从int开始,您可以将其转换为BarID:

Span foo=GetData; var结果=新巴里德[foo.Length/4]; 对于int i=0;i这里的切片步骤只是偏移了我们应该在跨度中开始读取的位置。

这里有趣的步骤是以小端的方式可靠地读取字节,这里的可靠意味着可以在任何CPU上工作,而不仅仅是碰巧是小端的CPU;幸运的是,BinaryPrimitives使这一点变得显而易见,它给您一个Span中的int,GetData中的byte[]可以隐式地转换为Span。然后从int开始,您可以将其转换为BarID:

Span foo=GetData; var结果=新巴里德[foo.Length/4]; 对于int i=0;i这里的切片步骤正好抵消了我们应该在跨度中开始阅读的位置。

尽管Marc的答案既好又快,但这只适用于.NET Core,或者如果您使用额外的NUGET。如果这可能是一个问题,或者您的目标是较旧的框架版本,则可以使用以下解决方案:

var bytes=新字节[]{0x01,0x0b,0x00,0x00,0x02,0x0b,0x00,0x00}; 返回BitConverter.IsLittleEndian ? ConvertLittleEndian字节 :ConvertBigEndianbytes; 其中转换方法:

私有静态不安全BarID[]convertleEndianByte[]字节 { var barIds=新的BarID[bytes.Length/4]; 固定字节*pBytes=字节 { BarID*asIds=BarID*pBytes; 对于int i=0;i 专用静态BarID[]ConvertBigEndianbyte[]字节 { var barIds=新的BarID[bytes.Length/4]; 对于int i=0;ibarIds[i]=BarIDbytes[offset]尽管Marc的答案既好又快,但这只适用于.NET Core,或者如果您使用其他NuGet。如果这可能是一个问题,或者您针对的是较旧的Framework版本,您可以使用如下解决方案:

var bytes=新字节[]{0x01,0x0b,0x00,0x00,0x02,0x0b,0x00,0x00}; 返回BitConverter.IsLittleEndian ?ConvertLittleEndianbytes :ConvertBigEndianbytes; 其中转换方法:

私有静态不安全BarID[]convertleEndianByte[]字节 { var barIds=新的BarID[bytes.Length/4]; 固定字节*pBytes=字节 { BarID*asIds=BarID*pBytes; 对于int i=0;i 专用静态BarID[]ConvertBigEndianbyte[]字节 { var barIds=新的BarID[bytes.Length/4]; 对于int i=0;ibarIds[i]=BarIDbytes[offset]为什么不使用字典呢?你可以将键值对存储在一起。在一个循环中,BarIDBitConverter.ToInt32foo[pos];,其中[pos]=0,4,…@Jimi如果我在32位系统上,这会有什么变化吗?枚举的大小会不同吗?不,它的大小相同。但是,看看Marc Gravell写的。B

为什么不用字典呢?您可以将KeyValuePairs存储到一起。在循环中,BarIDBitConverter.ToInt32foo,[pos];,其中[pos]=0,4,…@Jimi如果我在32位系统上,这会有什么变化吗?枚举的大小会不同吗?不,是相同的大小。但是,看看Marc Gravell写的。BitConverter.IsLittleEndiant这是不可靠的-它是CPU端;它也像任何东西一样分配,现在它在big-endian架构上分配得更多,至少:这是不可靠的-它是CPU-endian;它也像任何东西一样进行分配,现在它在big-endian体系结构上的分配甚至更多,至少:这比Tomas的答案有效得多,尽管它只在.NET内核或.NET标准2.1上工作。@GyörgyKőszeg不,这是错误的;从.NET 4.5/.NET Standard 1.1/到其他各种版本,它在任何地方都能工作-你只需要啊,你毕竟是对的,我的意思是不允许使用nuget软件包。@GyörgyKőszeg嗯,是的,但既然这是.NET中事实上的分发机制,不允许nuget软件包是非常愚蠢的:用愚蠢的方式做事:这比Tomas的答案有效得多,尽管它只在.NET内核或.NET标准2.1上有效。@GyörgyKőszeg不,这是错误的;从.NET 4.5/.NET Standard 1.1/到其他各种版本,它在任何地方都能工作-你只需要啊,你毕竟是对的,我的意思是不允许使用nuget软件包。@GyörgyKőszeg嗯,是的,但既然这是.NET中事实上的分发机制,禁止nuget软件包非常愚蠢:用愚蠢的方式做事:尽管Marc的答案既好又快,但这只适用于.NETCore这是完全错误的,;它几乎可以在任何地方工作,包括.NETFramework到4.5:-您只需要一个对系统的包引用。内存:答案已更正。也许这只是我的错,我不喜欢在我的库中添加许多第三方依赖项,这样它们只支持从.NET Core 3/Standard 2.1开始的Span及其朋友,尽管Marc的答案既好又快,但这只适用于.NET Core。-这是完全错误的,;它几乎可以在任何地方工作,包括.NETFramework到4.5:-您只需要一个对系统的包引用。内存:答案已更正。也许这只是我的错,我不喜欢在我的库中添加很多第三方依赖项,所以它们只支持从.NET Core 3/Standard 2.1开始的Span及其朋友