请教C#下如何序列化自定义结构或类?
各位高手:请问在C#中使用Binaryformater将自定义数据结构序列化时,如定义结构struct STData{int num;char type; short arr;double x;};结构采用单字节对齐方式,序列化后字节的数组比结构实际的字节数要大几倍,如何让序列化后字节数组的大小等于实际结构的字节数?由于需要C#通过UDP与VC6之间进行数据通信,在VC6中结构使用单字节对齐后,转换后的字节数组就是实际结构的字节之和,而如果C#转换后的字节数组大小与VC6的字节数组大小不同就无法确保C#与VC6之间的正常通信。请问如何解决这个问题?--------------------编程问答-------------------- 没搞过,帮你顶 --------------------编程问答-------------------- 最好自己处理,截取收到的数据包的每个字节,进行相应的转换后给的Struct赋值。别想着偷懒,有时候最笨的办法就是最有效的办法。 --------------------编程问答-------------------- 不是序列化数据结构,而是序列化数据。
在C#里实现方法:自定义标签,给需要序列化的属性贴上
然后用反射的方法读取这个struct或者class, 检索贴有此标签的属性,输出到xml流中
vc中通过udp传递过去的xml数据流,将数据填充到对应的struct或者class中去 --------------------编程问答-------------------- Binaryformater could add some meta-data, so the serialized result is more than the memory representation.
You might want to do this:
--------------------编程问答--------------------
[StructLayout(LayoutKind.Sequential, Pack=1)]
struct STData{
int num;
char type;
short arr;
double x;
};
class Program
{
static void Main()
{
STData data = new STData();
byte[] buf = new byte[Marshal.SizeOf(data)];
// copy STData into a byte array, it is easier than direct accessing a pinned memory
unsafe
{
byte* pB = (byte*)&data;
for (int i = 0; i < buf.Length; i++)
{
buf[i] = pB[i];
}
}
// now you may send the buf over UDP
}
}
定义类似如下的结构和类:
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct STCord
{
public double x; //
public double y;
public byte type;
public int num;
};
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class STPara
{
public byte type1; //
public int[] fre = new int[5]; //
public byte type2; //
};
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)] //
public class STChar
{
public byte num; //
public double Time; //
public STCord Pos; //
public int Pw; //
public STPara Para; //
public byte type; //
public STTargetCharacter()
{
Para= new STPara();
}
};
请教专家:按照您所给的方法确实能够实现将自定义结构转换为byte数组,但还有以下两个问题:
第一:如果对于自定义类类型如STChar,对其进行强制类型的转换byte* pB = (byte*)&data将报错“无法获取托管类型的地址和大小,或无法声明指向它的指针”,该如何将类变量STChar转换为byte数组呢?
第二:由于是C#与VC6之间进行相互通信,C#又该如何将接收到的byte数组转换为对应的结构或类呢?
C#中是否有比较简单的办法实现这些转换,而不需要对每一种自定义类型中的每个变量进行单独的转换后再合成为一个byte数组,否则一旦数据类型较多,转换的工作量会非常大,而且某一数据类型有变化,转换方法也必须做出相应的改变。谢谢! --------------------编程问答-------------------- 自己实现序列化接口,或者序列化抽象类。把所有要序列化的类或结构体转为byte数组传递
有两种类,一种是序列化后长度固定的类 。可以用更快速的 内存copy序列化。
#region 缓存正文索引类
/// <summary>
/// 缓存正文索引类
/// </summary>
internal sealed class FileCacheContentIndex
{
internal long PassTime = 0;//过期时间
internal long Key;//模板时间刻度
internal byte FileName = 0;//文件名
internal long IndexStart = 0;//本身的开始位置
internal byte NextFileName = 0;//下一个文件名
internal long NextIndexStart = 0;//下一个本身的开始位置 //为0 的时候关闭 不为0开启
internal long ContentIndexStart = 0;//正文开始位置 //为0 的时候关闭 不为0开启
internal uint ContentIndexLen = 0;//正文的长度
internal string UrlKey = null;//访问路径的key 带参数的url路径了。(len 32)
internal const int ClassLength = TempletCache.uintl + TempletCache.bytel * 2 + TempletCache.longl * 5 + 32;
internal byte IsChange = 0;//是否已经被修改了 0 没有 1 已经被修改了 这个应该是内存变量,不向文件内记录
//===============================================//
internal FileCacheContentIndex MFont = null;//内存中上一个正文索引类指向
internal FileCacheContentIndex MNext = null;//内存中下一个正文索引类指向
internal FileCacheContentIndex Font = null;//上一个正文索引类指向
internal FileCacheContentIndex Next = null;//下一个正文索引类指向
internal FileCacheContent Content = null;//缓存正文类
}
#endregion
这样对长度不变的类进行序列化和反序列化
#region 序列化“缓存正文索引类”
internal static byte[] SerializableContent(FileCacheContentIndex FC)
{
byte[] BA = new byte[FileCacheContentIndex.ClassLength];
int l = 0;
System.Buffer.BlockCopy(System.BitConverter.GetBytes(FC.PassTime), 0, BA, l, TempletCache.longl);
l += TempletCache.longl;
System.Buffer.BlockCopy(System.BitConverter.GetBytes(FC.Key), 0, BA, l, TempletCache.longl);
l += TempletCache.longl;
BA[l++] = FC.FileName;
System.Buffer.BlockCopy(System.BitConverter.GetBytes(FC.IndexStart), 0, BA, l, TempletCache.longl);
l += TempletCache.longl;
BA[l++] = FC.NextFileName;
System.Buffer.BlockCopy(System.BitConverter.GetBytes(FC.NextIndexStart), 0, BA, l, TempletCache.longl);
l += TempletCache.longl;
System.Buffer.BlockCopy(System.BitConverter.GetBytes(FC.ContentIndexStart), 0, BA, l, TempletCache.longl);
l += TempletCache.longl;
System.Buffer.BlockCopy(System.BitConverter.GetBytes(FC.ContentIndexLen), 0, BA, l, TempletCache.uintl);
l += TempletCache.uintl;
System.Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(FC.UrlKey), 0, BA, l, 32);
return BA;
}
#endregion
#region 反序列化“缓存正文索引类”
internal static FileCacheContentIndex InstanceContent(byte[] BA)
{
FileCacheContentIndex MA = new FileCacheContentIndex();
byte[] Bn = new byte[TempletCache.uintl];
byte[] Bnlong = new byte[TempletCache.longl];
int l = 0;
System.Buffer.BlockCopy(BA, l, Bnlong, 0, TempletCache.longl);
MA.PassTime = System.BitConverter.ToInt64(Bnlong, 0);
l += TempletCache.longl;
System.Buffer.BlockCopy(BA, l, Bnlong, 0, TempletCache.longl);
MA.Key = System.BitConverter.ToInt64(Bnlong, 0);
l += TempletCache.longl;
MA.FileName = BA[l++];
System.Buffer.BlockCopy(BA, l, Bnlong, 0, TempletCache.longl);
MA.IndexStart = System.BitConverter.ToInt64(Bnlong, 0);
l += TempletCache.uintl;
MA.NextFileName = BA[l++];
System.Buffer.BlockCopy(BA, l, Bnlong, 0, TempletCache.longl);
MA.NextIndexStart = System.BitConverter.ToInt64(Bnlong, 0);
l += TempletCache.uintl;
System.Buffer.BlockCopy(BA, l, Bnlong, 0, TempletCache.longl);
MA.ContentIndexStart = System.BitConverter.ToInt64(Bnlong, 0);
l += TempletCache.uintl;
System.Buffer.BlockCopy(BA, l, Bn, 0, TempletCache.uintl);
MA.ContentIndexLen = System.BitConverter.ToUInt32(Bn, 0);
l += TempletCache.uintl;
byte[] Ba1 = new byte[32];
System.Buffer.BlockCopy(BA, l, Ba1, 0, 32);
MA.UrlKey = System.Text.Encoding.ASCII.GetString(Ba1);
return MA;
}
#endregion
序列化后长度不固定的类。就只能实现序列化接口了
#region 序列化基础抽象类
internal abstract class SerializableAbstractClass
{
internal abstract void Load(BinaryReader rdr);
internal abstract void Save(BinaryWriter wrt);
}
#endregion
#region 缓存正文类
/// <summary>
/// 缓存正文类
/// </summary>
internal sealed class FileCacheContent : SerializableAbstractClass
{
internal byte IsArray = 0;//是否有局部缓存 1全局 2局部
internal ushort Count = 0;//数组的长度
internal string Content = null;//全局正文缓存 //局部的时候位空
internal string[] ConArray = null;//局部缓存
#region 重载实现读函数
/// <summary>
/// 重载实现读函数
/// </summary>
/// <param name="rdr"></param>
internal override void Load(BinaryReader rdr)
{
IsArray = rdr.ReadByte();
Count = rdr.ReadUInt16();
if (IsArray == 1)
{
Content = rdr.ReadString();
}
else
{
ConArray = new string[Count];
for (int i = 0; i < Count; i++)
{
ConArray[i] = rdr.ReadString();
}
}
}
#endregion
#region 重载实现写函数
/// <summary>
/// 重载实现写函数
/// </summary>
/// <param name="wrt"></param>
internal override void Save(BinaryWriter wrt)
{
wrt.Write(IsArray);
wrt.Write(Count);
if (IsArray == 1)
{
wrt.Write(Content);
}
else
{
for (int i = 0; i < ConArray.Length; i++)
{
wrt.Write(ConArray[i]);
}
}
}
#endregion
}
#endregion
这样序列化和反序列化
#region 变长数据函数
internal static byte[] WriteData(SerializableAbstractClass SI)
{
byte[] BA = null;
MemoryStream fs = null; BinaryWriter wrt = null;
try
{
fs = new MemoryStream(500);
wrt = new BinaryWriter(fs);
SI.Save(wrt);
BA = fs.ToArray();
return BA;
}
catch
{
return new byte[0];
}
finally
{
wrt.Close();
fs.Close();
}
}
#endregion
#region 读数组
internal static bool ReadData(SerializableAbstractClass SI, byte[] BA)
{
MemoryStream fs = null; BinaryReader read = null;
try
{
fs = new MemoryStream(BA);
read = new BinaryReader(fs);
SI.Load(read);
return true;
}
catch
{
return false;
}
finally
{
read.Close();
fs.Close();
}
}
#endregion
--------------------编程问答-------------------- 其实也可以很简单的用结构处理,只是要记得通知数组的大小:
--------------------编程问答-------------------- 如果是使用.net二进制序列化,本身就应该由.net进行反序列化,因为这个序列化是.net的标准。
class Program
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct STCord
{
public double x;
public double y;
public byte type;
public int num;
};
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct STPara
{
public byte type1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=5)] //<-----
public int[] fre;
public byte type2;
};
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct STChar
{
public byte num;
public double Time;
public STCord Pos;
public int Pw;
public STPara Para;
public byte type;
};
static void Main(string[] args)
{
int size = Marshal.SizeOf(typeof(STChar)); // size == 57
}
}
如果你是跨语言的序列化建议采用xml序列化形式,例如与java通讯,甚至和VC++,因为这样的序列化对象比较
不是简单的内存数据传导,而是一个对象的反序列化实例,即使它是值类型。
如果你确实想用二进制的序列化和其他语言的通讯,可以自己完成序列化的过程,但是这个我认为没有比较转换为
非托管结构。
--------------------编程问答-------------------- 对于结构可以这样处理,对于类变量该如何处理呢?另外C#又如何将接收到的byte数组转换为对应的结构或类呢?感谢yanqing5266的帮助,你的方法还没有试,不知道转换后的字节是否与VC6的memcpy命令转换后的字节数相同,不然不能实现C#与VC6之间的相互通信。谢谢zhujiechang,XML序列化也增加了无数类容,无法用VC6去正确解包,并且影响通信速度。
--------------------编程问答--------------------
对于结构可以这样处理,对于类变量该如何处理呢?另外C#又如何将接收到的byte数组转换为对应的结构或类呢?感谢yanqing5266的帮助,你的方法还没有试,不知道转换后的字节是否与VC6的memcpy命令转换后的字节数相同,不然不能实现C#与VC6之间的相互通信。谢谢zhujiechang,XML序列化也增加了无数类容,无法用VC6去正确解包,并且影响通信速度。
补充:.NET技术 , C#