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

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






转载于:https://www.cnblogs.com/janic716/archive/2012/01/03/janic716.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值