C#序列化链表

时间:2022-03-30 08:57:50

场景简化:
    程序中的数据每隔1s实时获得的,存储到链表里,需要及时保存到文件里去。
    之前的方法是把链表对象序列化到文件里去,好处是不需要太多处理,不用管链表中是否有元素(相对于后面的第三种方法而言)。可是这样有个问题,每次都得把整个链表序列化到文件里去,当数据多了之后开销挺大的。直觉上应该是每次只把新增的数据追加到文件里就可以了。

  为了简洁起见,把异常处理,局部变量声明等的都去了。每次只贴出修改过的类的代码,MeasuredValue是一个类,存储了采集到的数据。

  第一版的代码如下:

1 public static class FileSerializer 2 { 3 //stream是调用者负责关闭的 4 public static void Serialize(FileStream stream, object objectToSerialize) 5 { 6 BinaryFormatter bFormatter = new BinaryFormatter(); 7 bFormatter.Serialize(stream, objectToSerialize); 8 } 9 public static T Deserialize<T>(FileStream stream) 10 { 11 T objectToDeSerialize = default(T); 12 BinaryFormatter bFormatter = new BinaryFormatter(); 13 objectToDeSerialize = (T)bFormatter.Deserialize(stream); 14 return objectToDeSerialize; 15 } 16 } 17 class Serialize 18 { 19 public bool SerializeMeasuredData(List<MeasuredValue> values){ 20 SerializeHelper data = new SerializeHelper(values); 21 FileSerializer.Serialize(stream, data); 22 } 23 public bool DeSerializeMeasuredData(List<MeasuredValue> values) 24 { 25 SerializeHelper data = null; 26 stream = File.Open(_fileName, FileMode.Open, FileAccess.Read); 27 data = FileSerializer.Deserialize<SerializeHelper>(stream); 28 values = data; 29 } 30 31} 32 //33 //想在序列化时,序列化某些指定的List,并添加了别的信息所以加了这个SerializeHelper。 34 class SerializeHelper(){ 35 List<MeasuredValue> _values; 36 public SerializeHelper(List<MeasuredValue> values){ 37 _values = values; 38 } 39 public void GetObjectData(SerializationInfo info, StreamingContext ctxt) 40 { 41 info.AddValue("VALUES", _values, typeof(List<MeasuredValue>)); 42 } 43 private SerializeHelper(SerializationInfo info, StreamingContext context) 44 { 45 _values = (List<MeasuredValue>)info.GetValue(_values, typeof(List<MeasuredValue>)); 46 } 47 48 }

  后来想实现每次只把增加的采集数据序列化到文件里。我不确定思路对不对,所以使用快猛糙的方法先实现了一下。

  第二版代码如下:

1 class Serialize 2 { 3 uint _id; 4 public bool SerializeMeasuredData(List<MeasuredValue> values){ 5 SerializeHelper data = new SerializeHelper(values, _id); 6 _id++; 7 FileSerializer.Serialize(stream, data); 8 } 9 public bool DeSerializeMeasuredData(List<MeasuredValue> values) 10 { 11 SerializeHelper data = null; 12 stream = File.Open(_fileName, FileMode.Open, FileAccess.Read); 13 data = FileSerializer.Deserialize<SerializeHelper>(stream); 14 values = data; 15 } 16 } 17 18 19 class SerializeHelper(){ 20 List<MeasuredValue> _values; 21 _id; 22 public SerializeHelper(List<MeasuredValue> values, uint id){ 23 _values = values; 24 _id = id; 25 } 26 27 public void GetObjectData(SerializationInfo info, StreamingContext ctxt) 28 { 29 info.AddValue("VALUES"+_id.ToString(), _values[_values.Length-1], typeof(MeasuredValue)); 30 } 31 //反序列化时id却由SerializeHelper来维护,因为不可能通过参数传递过来 32 private SerializeHelper(SerializationInfo info, StreamingContext context) 33 { 34 _id = 0; 35 MeasuredValue mv= null; 36 _values = new List<MeasuredValue>(); 37 try{ 38 while(true){ 39 mv = (MeasuredValue)info.GetValue("VALUES"+id.ToString(), typeof(MeasuredValue)); 40 _values.Add(mv); 41 } 42 } 43 catch(Exception ex){//出现异常说明到文件末尾 44 45 } 46 } 47 }

  可是测试时出错了,不能正常序列化出链表。各位可以猜到我的问题出在哪里吗?

  调式时发现SerializeHelper(SerializationInfo info, StreamingContext context)这个函数中的info变量里,MemberCount值为1, MemberNames[4]中只有MemberNames[0]不会空,是"VALUES0",这样就惨了,说明 SerializeHelper里反序列化和序列化时是对应的。由于序列化时是只写入了一个MeasuredValue,所以反序列化时,只能获得一个MeasuredValue,,所以生成链表的操作不能在这个类里进行。对于SerializationInfo.AddValue(String name, object value, Type, type)时的name这个参数的作用,我很是疑惑,看样子这个name不是全局范围的标识符,在上面的例子中,是仅在代表一个SerializeHelper的字节流中起标识符的作用?
    本来感觉挺麻烦的,按照我一贯直来直往的思路,需要把List一部分一部分的序列化,之前就搜过,没啥资料,而且我也不知道该怎么组织关键字去搜。后来我想到,可以把必须使用的id封装到一个类中,把链表拆分成单个的元素,序列化时每次写入新增的元素,而反序列化时,把每个元素还原出来,然后根据元素的属性来组装到各自对应的链表中。

  新增了一个类ToSeriaMV,把需要的ListName信息和MeasuredValue封装起来,每次序列化和反序列的对象都是它。

  第三版代码如下: