尽管在C#中结构与类有着惊人的相似度,但在实际应用中,会常常因为一些特殊之类而错误的使用它,下面几点内容是笔者认为应该注意的:
对于结构:
1)可以有方法与属性
2)是密封的,不能被继承,或继承其他结构
3)结构隐式地继承自System.ValueType
4)结构有默认的无参数构造函数,可以将每个字段初始化为默认值,但这个默认的构造函数不能被替换,即使重载了带参数的构造函数
5)结构没有析构函数
6)除了const成员外,结构的字段不能在声明结构时初始化
7)结构是值类型,在定义时(尽管也使用new运算符)会分配堆栈空间,其值也存储于堆栈
8)结构主要用于小的数据结构,为了更好的性能,不要使用过于庞大的结构
9)可以像类那样为结构提供 Close() 或 Dispose() 方法
如果经常做通信方面的程序,结构体是非常有用的(为了更有效地组织数据,建议使用结构体),也会遇到字节数据与结构体相互转化的问题,下面是一般解决方法:
如何定义一个按字节顺序存储的结构体?
1 [StructLayout(LayoutKind.Sequential, Pack = 1)] //顺序排列,并按1字节对齐 (这里保存的是一组飞机参数,共73字节) 2 public struct StructPlane 3 { 4 public byte serialNum; 5 public double pitch; 6 public double roll; 7 public double yaw; 8 public double pitchVel; 9 public double rollVel; 10 public double yawVel; 11 public double alt; 12 public double vz; 13 public ushort pwm1; 14 public ushort pwm2; 15 public ushort pwm3; 16 public ushort pwm4; 17 };
结构体转字节数组的方法:
注:
1. 一般PC用小端模式(即高位存放于高地址,低位存放于低地址,反之为大端模式)存放数据,如果另一通讯设备用大端存储数据,则相互转化时就要注意了。
2.结构体需按上面的方式,顺序排列并按1字节对齐,下面的所有方法都一样。
1 //需添加的引用,提供Marshal类 2 using System.Runtime.InteropServices; 3 4 /// <summary> 5 /// 结构体转字节数组(按小端模式) 6 /// </summary> 7 /// <param name="obj">struct type</param> 8 /// <returns></returns> 9 byte[] StructureToByteArray(object obj) 10 { 11 int len = Marshal.SizeOf(obj); 12 byte[] arr = new byte[len]; 13 IntPtr ptr = Marshal.AllocHGlobal(len); 14 Marshal.StructureToPtr(obj, ptr, true); 15 Marshal.Copy(ptr, arr, 0, len); 16 Marshal.FreeHGlobal(ptr); 17 return arr; 18 } 19 20 /// <summary> 21 /// 结构体转字节数组(按大端模式) 22 /// </summary> 23 /// <param name="obj">struct type</param> 24 /// <returns></returns> 25 byte[] StructureToByteArrayEndian(object obj) 26 { 27 object thisBoxed = obj; //copy ,将 struct 装箱 28 Type test = thisBoxed.GetType(); 29 30 int offset = 0; 31 byte[] data = new byte[Marshal.SizeOf(thisBoxed)]; 32 33 object fieldValue; 34 TypeCode typeCode; 35 byte[] temp; 36 // 列举结构体的每个成员,并Reverse 37 foreach (var field in test.GetFields()) 38 { 39 fieldValue = field.GetValue(thisBoxed); // Get value 40 41 typeCode = Type.GetTypeCode(fieldValue.GetType()); // get type 42 43 switch (typeCode) 44 { 45 case TypeCode.Single: // float 46 { 47 temp = BitConverter.GetBytes((Single)fieldValue); 48 Array.Reverse(temp); 49 Array.Copy(temp, 0, data, offset, sizeof(Single)); 50 break; 51 } 52 case TypeCode.Int32: 53 { 54 temp = BitConverter.GetBytes((Int32)fieldValue); 55 Array.Reverse(temp); 56 Array.Copy(temp, 0, data, offset, sizeof(Int32)); 57 break; 58 } 59 case TypeCode.UInt32: 60 { 61 temp = BitConverter.GetBytes((UInt32)fieldValue); 62 Array.Reverse(temp); 63 Array.Copy(temp, 0, data, offset, sizeof(UInt32)); 64 break; 65 } 66 case TypeCode.Int16: 67 { 68 temp = BitConverter.GetBytes((Int16)fieldValue); 69 Array.Reverse(temp); 70 Array.Copy(temp, 0, data, offset, sizeof(Int16)); 71 break; 72 } 73 case TypeCode.UInt16: 74 { 75 temp = BitConverter.GetBytes((UInt16)fieldValue); 76 Array.Reverse(temp); 77 Array.Copy(temp, 0, data, offset, sizeof(UInt16)); 78 break; 79 } 80 case TypeCode.Int64: 81 { 82 temp = BitConverter.GetBytes((Int64)fieldValue); 83 Array.Reverse(temp); 84 Array.Copy(temp, 0, data, offset, sizeof(Int64)); 85 break; 86 } 87 case TypeCode.UInt64: 88 { 89 temp = BitConverter.GetBytes((UInt64)fieldValue); 90 Array.Reverse(temp); 91 Array.Copy(temp, 0, data, offset, sizeof(UInt64)); 92 break; 93 } 94 case TypeCode.Double: 95 { 96 temp = BitConverter.GetBytes((Double)fieldValue); 97 Array.Reverse(temp); 98 Array.Copy(temp, 0, data, offset, sizeof(Double)); 99 break; 100 } 101 case TypeCode.Byte: 102 { 103 data[offset] = (Byte)fieldValue; 104 break; 105 } 106 default: 107 { 108 //System.Diagnostics.Debug.Fail("No conversion provided for this type : " + typeCode.ToString()); 109 break; 110 } 111 }; // switch 112 if (typeCode == TypeCode.Object) 113 { 114 int length = ((byte[])fieldValue).Length; 115 Array.Copy(((byte[])fieldValue), 0, data, offset, length); 116 offset += length; 117 } 118 else 119 { 120 offset += Marshal.SizeOf(fieldValue); 121 } 122 } // foreach 123 124 return data; 125 } // Swap
字节数组转结构体的方法:
/// <summary> /// 字节数组转结构体(按小端模式) /// </summary> /// <param name="bytearray">字节数组</param> /// <param name="obj">目标结构体</param> /// <param name="startoffset">bytearray内的起始位置</param> public static void ByteArrayToStructure(byte[] bytearray, ref object obj, int startoffset) {
int len = Marshal.SizeOf(obj); IntPtr i = Marshal.AllocHGlobal(len); // 从结构体指针构造结构体 obj = Marshal.PtrToStructure(i, obj.GetType()); try { // 将字节数组复制到结构体指针 Marshal.Copy(bytearray, startoffset, i, len); } catch (Exception ex) { Console.WriteLine("ByteArrayToStructure FAIL: error " + ex.ToString()); } obj = Marshal.PtrToStructure(i, obj.GetType()); Marshal.FreeHGlobal(i); //释放内存,与 AllocHGlobal() 对应 } /// <summary> /// 字节数组转结构体(按大端模式) /// </summary> /// <param name="bytearray">字节数组</param> /// <param name="obj">目标结构体</param> /// <param name="startoffset">bytearray内的起始位置</param> public static void ByteArrayToStructureEndian(byte[] bytearray, ref object obj, int startoffset) { int len = Marshal.SizeOf(obj); IntPtr i = Marshal.AllocHGlobal(len); byte[] temparray = (byte[])bytearray.Clone(); // 从结构体指针构造结构体 obj = Marshal.PtrToStructure(i, obj.GetType()); // 做大端转换 object thisBoxed = obj; Type test = thisBoxed.GetType(); int reversestartoffset = startoffset; // 列举结构体的每个成员,并Reverse foreach (var field in test.GetFields()) { object fieldValue = field.GetValue(thisBoxed); // Get value TypeCode typeCode = Type.GetTypeCode(fieldValue.GetType()); //Get Type if (typeCode != TypeCode.Object) //如果为值类型 { Array.Reverse(temparray, reversestartoffset, Marshal.SizeOf(fieldValue)); reversestartoffset += Marshal.SizeOf(fieldValue); } else //如果为引用类型 { reversestartoffset += ((byte[])fieldValue).Length; } } try { //将字节数组复制到结构体指针 Marshal.Copy(temparray, startoffset, i, len); } catch (Exception ex) { Console.WriteLine("ByteArrayToStructure FAIL: error " + ex.ToString()); } obj = Marshal.PtrToStructure(i, obj.GetType()); Marshal.FreeHGlobal(i); //释放内存 }
使用示例一:
... ...
byte[] packet = new byte[73]{...};
StructPlane structPlane = new StructPlane();
object structType = structPlane; ByteArrayToStructure(packet, ref structType, 0);
使用示例二:
StructPlane structPlane = new StructPlane();
structPlane.serialNum = ...;
structPlane.time = ...;
structPlane.pitch = ...;
... ...
byte[] datas = StructureToByteArray(structPlane);
转自:http://www.360doc.com/content/12/0129/15/8527479_182507962.shtml
浙公网安备 33010602011771号