C#利用目标类反射读取非Windows设备生成的二进制文件问题求助
解决方案
你遇到的报错本质是BinaryReader.ReadBytes()返回的是byte[]类型,无法直接赋值给非字节数组的其他值类型字段,同时原有的数组赋值逻辑也无法适配非byte[]的数组类型(比如float[,]这类C风格二维数组)。以下方案可实现无分支、无unsafe、无需频繁修改结构体定义的通用解析:
1. 封装通用字节转值类型方法
支持基础值类型、枚举、自定义结构体,同时兼容跨设备字节序差异:
// 通用字节数组转目标类型方法,isLittleEndian参数根据非Windows设备实际字节序设置 private static object ConvertFromBytes(byte[] buffer, Type targetType, bool isLittleEndian = false) { // 字节序适配:设备字节序与当前系统不一致时反转字节 if (BitConverter.IsLittleEndian != isLittleEndian) { Array.Reverse(buffer); } TypeCode typeCode = Type.GetTypeCode(targetType); return typeCode switch { TypeCode.Boolean => BitConverter.ToBoolean(buffer, 0), TypeCode.Char => BitConverter.ToChar(buffer, 0), TypeCode.SByte => (sbyte)buffer[0], TypeCode.Byte => buffer[0], TypeCode.Int16 => BitConverter.ToInt16(buffer, 0), TypeCode.UInt16 => BitConverter.ToUInt16(buffer, 0), TypeCode.Int32 => BitConverter.ToInt32(buffer, 0), TypeCode.UInt32 => BitConverter.ToUInt32(buffer, 0), TypeCode.Int64 => BitConverter.ToInt64(buffer, 0), TypeCode.UInt64 => BitConverter.ToUInt64(buffer, 0), TypeCode.Single => BitConverter.ToSingle(buffer, 0), TypeCode.Double => BitConverter.ToDouble(buffer, 0), // 自动处理枚举类型 _ when targetType.IsEnum => Enum.ToObject(targetType, ConvertFromBytes(buffer, Enum.GetUnderlyingType(targetType), isLittleEndian)), // 自动处理自定义结构体,无需提前定义Marshal特性 _ => Marshal.PtrToStructure(Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0), targetType) }; }
2. 封装多维数组转换方法
无需unsafe代码即可支持C风格二维/多维数组解析:
// 通用字节数组转任意维度数组方法 private static Array ConvertArrayFromBytes(byte[] buffer, Type arrayType, Array existingArray, bool isLittleEndian = false) { Type elementType = arrayType.GetElementType(); int elementSize = Marshal.SizeOf(elementType); int bufferOffset = 0; // 遍历多维数组所有索引位置赋值 foreach (var indices in GetAllArrayIndices(existingArray)) { byte[] elementBuf = new byte[elementSize]; Buffer.BlockCopy(buffer, bufferOffset, elementBuf, 0, elementSize); existingArray.SetValue(ConvertFromBytes(elementBuf, elementType, isLittleEndian), indices); bufferOffset += elementSize; } return existingArray; } // 辅助方法:获取多维数组的所有索引组合 private static IEnumerable<int[]> GetAllArrayIndices(Array array) { int[] indices = new int[array.Rank]; IEnumerable<int[]> result = new[] { indices }; for (int i = 0; i < array.Rank; i++) { int dim = i; result = result.SelectMany( prev => Enumerable.Range(0, array.GetLength(dim)), (prev, idx) => prev.Take(dim).Append(idx).Concat(prev.Skip(dim + 1)).ToArray() ); } return result; }
3. 替换原有遍历赋值逻辑
Type type = myData.GetType(); BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static; FieldInfo[] fields = type.GetFields(flags); // 非Windows设备通常为大端,此处根据实际情况调整 const bool DeviceIsLittleEndian = false; foreach (FieldInfo field in fields) { if (field.FieldType.IsArray) { Array existArray = (Array)field.GetValue(myData); int elementSize = Marshal.SizeOf(field.FieldType.GetElementType()); int totalBytes = existArray.Length * elementSize; byte[] buffer = br.ReadBytes(totalBytes); field.SetValue(myData, ConvertArrayFromBytes(buffer, field.FieldType, existArray, DeviceIsLittleEndian)); } else { int readSize = Marshal.SizeOf(field.FieldType); if (readSize > 0) { byte[] buffer = br.ReadBytes(readSize); field.SetValue(myData, ConvertFromBytes(buffer, field.FieldType, DeviceIsLittleEndian)); } } }
扩展优化建议
如果后续固件升级导致结构变动,只需要给不同版本的类的字段添加自定义特性,标记字段顺序、数组维度等信息,解析时通过反射读取特性即可,无需修改核心解析逻辑,可维护性极高。
内容的提问来源于stack exchange,提问作者VariableSquid
相关产品推荐
相关产品推荐

