导航

[转]C#中的结构体,以及结构体与字节数组的互转化

Posted on 2014-04-14 21:57  FryFish  阅读(669)  评论(0)    收藏  举报

尽管在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