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

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数组:
        ////


        /// 结构体转byte数组
        ///

        /// 要转换的结构体
        /// 转换后的byte数组
        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数组转结构体:
        ///
        /// byte数组转结构体
        ///

        /// byte数组
        /// 结构体类型
        /// 转换后的结构体
        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/// 
  5/// 结构体转字节数组(按小端模式)
6/// 
  7/// struct type
  8/// 
  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         /// 
 21/// 结构体转字节数组(按大端模式)
 22/// 
 23/// struct type
 24/// 
 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                 }; // switch
112if (typeCode == TypeCode.Object)
113                 {
114int length = ((byte[])fieldValue).Length;
115                     Array.Copy(((byte[])fieldValue), 0, data, offset, length);
116                     offset += length;
117                 }
118else
119                 {
120                     offset += Marshal.SizeOf(fieldValue);
121                 }
122             } // foreach
123 
124return data;
125         } // Swap


字节数组转结构体的方法:

/// 
/// 字节数组转结构体(按小端模式)
/// 
/// 字节数组
/// 目标结构体
/// bytearray内的起始位置
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() 对应

}

/// 
/// 字节数组转结构体(按大端模式)
/// 
/// 字节数组
/// 目标结构体
/// bytearray内的起始位置
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.haogongju.net/art/1200066

你可能感兴趣的:(C#,windows)