C#结构体和字节数组的转换

来源:互联网 发布:淘宝的秒杀在哪里 编辑:程序博客网 时间:2024/05/09 20:21

http://www.haogongju.net/art/624248

在写C#TCP通信程序时,发送数据时,只能发送byte数组,处理起来比较麻烦不说,如果是和VC6.0等写的程序通信的话,很多的都是传送结构体,在VC6.0中可以很方便的把一个char[]数组转换为一个结构体,而在C#却不能直接把byte数组转换为结构体,要在C#中发送结构体,可以按以下方法实现:
1)定义结构体:
    //命名空间
    using System.Runtime.InteropServices;

//注意这个属性不能少
    [StructLayoutAttribute(LayoutKind.Sequential,CharSet=CharSet.Ansi,Pack=1)]
    struct TestStruct
    {
        public int c;
        //字符串,SizeConst为字符串的最大长度
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
        public string str;
        //int数组,SizeConst表示数组的个数,在转换成
        //byte数组前必须先初始化数组,再使用,初始化
        //的数组长度必须和SizeConst一致,例test = new int[6];
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
        public int[] test;
    }

2)结构体转byte数组:
        //// <summary>
        /// 结构体转byte数组
        /// </summary>
        /// <param name="structObj">要转换的结构体</param>
        /// <returns>转换后的byte数组</returns>
        public static byte[] StructToBytes(object structObj)
       {
            //得到结构体的大小
            int size = Marshal.SizeOf(structObj);
            //创建byte数组
            byte[] bytes = new byte[size];
            //分配结构体大小的内存空间
            IntPtr structPtr = Marshal.AllocHGlobal(size);
            //将结构体拷到分配好的内存空间
            Marshal.StructureToPtr(structObj, structPtr, false);
            //从内存空间拷到byte数组
            Marshal.Copy(structPtr, bytes, 0, size);
            //释放内存空间
            Marshal.FreeHGlobal(structPtr);
            //返回byte数组
            return bytes;
        }
3byte数组转结构体:
        /// <summary>
        /// byte数组转结构体
        /// </summary>
        /// <param name="bytes">byte数组</param>
        /// <param name="type">结构体类型</param>
        /// <returns>转换后的结构体</returns>
        public static object BytesToStuct(byte[] bytes,Type type)
        {
            //得到结构体的大小
            int size = Marshal.SizeOf(type);
            //byte数组长度小于结构体的大小
            if (size > bytes.Length)
            {
                //返回空
                return null;
            }
            //分配结构体大小的内存空间
            IntPtr structPtr = Marshal.AllocHGlobal(size);
            //byte数组拷到分配好的内存空间
            Marshal.Copy(bytes,0,structPtr,size);
            //将内存空间转换为目标结构体
            object obj = Marshal.PtrToStructure(structPtr, type);
            //释放内存空间
            Marshal.FreeHGlobal(structPtr);
            //返回结构体
            return obj;
        }

尽管在C#中结构与类有着惊人的相似度,但在实际应用中,会常常因为一些特殊之类而错误的使用它,下面几点内容是笔者认为应该注意的:

对于结构

1)可以有方法与属性
2)是密封的,不能被继承,或继承其他结构
3)结构隐式地继承自System.ValueType
4)结构有默认的无参数构造函数,可以将每个字段初始化为默认值,但这个默认的构造函数不能被替换,即使重载了带参数的构造函数
5)结构没有析构函数
6)除了const成员外,结构的字段不能在声明结构时初始化
7)结构是值类型,在定义时(尽管也使用new运算符)会分配堆栈空间,其值也存储于堆栈
8)结构主要用于小的数据结构,为了更好的性能,不要使用过于庞大的结构
9)可以像类那样为结构提供 Close() 或 Dispose() 方法

如果经常做通信方面的程序,结构体是非常有用的(为了更有效地组织数据,建议使用结构体),也会遇到字节数据与结构体相互转化的问题,下面是一般解决方法:

如何定义一个按字节顺序存储的结构体?

 1     [StructLayout(LayoutKind.Sequential, Pack = 1)]  //顺序排列,并按1字节对齐 (这里保存的是一组飞机参数,共73字节) 2public struct StructPlane 3     {
 4public byte serialNum; 5public double pitch; 6public double roll; 7public double yaw; 8public double pitchVel; 9public double rollVel;10public double yawVel;11public double alt;12public double vz;13public ushort pwm1;14public ushort pwm2;15public ushort pwm3;16public 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>  9byte[] StructureToByteArray(object obj) 10         { 11int len = Marshal.SizeOf(obj); 12byte[] 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); 17return arr; 18         } 19 20         /// <summary> 21/// 结构体转字节数组(按大端模式) 22/// </summary> 23/// <param name="obj">struct type</param> 24/// <returns></returns> 25byte[] StructureToByteArrayEndian(object obj) 26         { 27object thisBoxed = obj;   //copy ,将 struct 装箱 28             Type test = thisBoxed.GetType(); 29  30int offset = 0; 31byte[] data = new byte[Marshal.SizeOf(thisBoxed)]; 32  33object fieldValue; 34             TypeCode typeCode; 35byte[] temp; 36// 列举结构体的每个成员,并Reverse 37foreach (var field in test.GetFields()) 38             { 39                 fieldValue = field.GetValue(thisBoxed); // Get value 40  41                 typeCode = Type.GetTypeCode(fieldValue.GetType());  // get type 42  43switch (typeCode) 44                 { 45case TypeCode.Single: // float 46                         { 47                             temp = BitConverter.GetBytes((Single)fieldValue); 48                             Array.Reverse(temp); 49                             Array.Copy(temp, 0, data, offset, sizeof(Single)); 50break; 51                         } 52case TypeCode.Int32: 53                         { 54                             temp = BitConverter.GetBytes((Int32)fieldValue); 55                             Array.Reverse(temp); 56                             Array.Copy(temp, 0, data, offset, sizeof(Int32)); 57break; 58                         } 59case TypeCode.UInt32: 60                         { 61                             temp = BitConverter.GetBytes((UInt32)fieldValue); 62                             Array.Reverse(temp); 63                             Array.Copy(temp, 0, data, offset, sizeof(UInt32)); 64break; 65                         } 66case TypeCode.Int16: 67                         { 68                             temp = BitConverter.GetBytes((Int16)fieldValue); 69                             Array.Reverse(temp); 70                             Array.Copy(temp, 0, data, offset, sizeof(Int16)); 71break; 72                         } 73case TypeCode.UInt16: 74                         { 75                             temp = BitConverter.GetBytes((UInt16)fieldValue); 76                             Array.Reverse(temp); 77                             Array.Copy(temp, 0, data, offset, sizeof(UInt16)); 78break; 79                         } 80case TypeCode.Int64: 81                         { 82                             temp = BitConverter.GetBytes((Int64)fieldValue); 83                             Array.Reverse(temp); 84                             Array.Copy(temp, 0, data, offset, sizeof(Int64)); 85break; 86                         } 87case TypeCode.UInt64: 88                         { 89                             temp = BitConverter.GetBytes((UInt64)fieldValue); 90                             Array.Reverse(temp); 91                             Array.Copy(temp, 0, data, offset, sizeof(UInt64)); 92break; 93                         } 94case TypeCode.Double: 95                         { 96                             temp = BitConverter.GetBytes((Double)fieldValue); 97                             Array.Reverse(temp); 98                             Array.Copy(temp, 0, data, offset, sizeof(Double)); 99break;100                         }101case TypeCode.Byte:102                         {103                             data[offset] = (Byte)fieldValue;104break;105                         }106default:107                         {108//System.Diagnostics.Debug.Fail("No conversion provided for this type : " + typeCode.ToString());109break;110                         }111                 }; // switch112if (typeCode == TypeCode.Object)113                 {114int length = ((byte[])fieldValue).Length;115                     Array.Copy(((byte[])fieldValue), 0, data, offset, length);116                     offset += length;117                 }118else119                 {120                     offset += Marshal.SizeOf(fieldValue);121                 }122             } // foreach123 124return 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;// 列举结构体的每个成员,并Reverseforeach (var field in test.GetFields())            {object fieldValue = field.GetValue(thisBoxed); // Get value                TypeCode typeCode = Type.GetTypeCode(fieldValue.GetType());  //Get Typeif (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.haogongju.net/art/1200066

原创粉丝点击