.NET序列化的一点技巧(附Demo)
阅读目录
介绍
序列化是将对象状态转换为可保持或传输的形式的过程。序列化的补集是反序列化,后者将流转换为对象。这两个过程一起保证数据易于存储和传输。
.NET Framework 提供了两个序列化技术:
-
二进制序列化保持类型保真,这对于多次调用应用程序时保持对象状态非常有用。例如,通过将对象序列化到剪贴板,可在不同的应用程序之间共享对象。您可以将对象序列化到流、磁盘、内存和网络等。远程处理使用序列化,“按值”在计算机或应用程序域之间传递对象。
-
XML 序列化只序列化公共属性和字段,并且不保持类型保真。当您希望提供或使用数据而不限制使用该数据的应用程序时,这一点非常有用。由于 XML 是开放式的标准,因此它对于通过 Web 共享数据来说是一个理想选择。SOAP 同样是开放式的标准,这使它也成为一个理想选择。
详细
具体命名空间:
-
包含可用于序列化和反序列化对象的类。(包括System.Runtime.Serialization.Formatters.Binary.BinaryFormatter,还有WCF中用到的DataContractAttribute, DataMemberAttribute)
-
包含可用于将对象序列化为 XML 格式的文档或流的类。
WCF大家很熟悉,一般的书籍都对基础的东西有很多内容的讲解,这里就对非WCF的一些序列化技巧抛个砖。
一:序列化标记
需要序列化:[Serializable]也可以写成[SerializableAttribute]
特性“Serializable”只在“class, struct, enum, delegate”声明中有效。所以只能在类,结构体等上面标记。
不需要序列化:[NonSerialized]也可以写成[NonSerializedAttribute]
特性“NonSerialized”只在“field”声明中有效。所以只能在字段上标记,连属性器都不行。
二:特殊情况:
事件的标记:
[field: NonSerializedAttribute()],需要加入field标记。
属性的标记:
其实我们叫的属性是属性器,是一对Get,Set方法。既然是方法,当然不是字段了,所以是不能序列化标记或者排除的,那我们怎么去处理某些属性不需要序列化的情况列。方法也是有的,需要把属性器中的Get,Set方法写实,即该有的字段还是得定义,不能偷懒,然后在该有的字段上面标记为不需要序列化。
三:序列化的特殊用法
学过C#的时候,大家都知道了个值类型,引用类型的概念,也可能知道了ICloneable这个接口,这个克隆接口可以复制对象,如实例化个student,然后调用Clone()即可以得到该对线的浅层副本。
浅层克隆就是只把改对象的值类型和引用类型的地址复制了,但是,原来对象中的被引用类型的对象发生改变,比如:student类中有个classroom,classroom中的某个字段发生改变,这样克隆后的对线的classroom的值也是会变的。除非classroom这个类也实现ICloneable接口。
序列化克隆的好处就是不用考虑浅层复制,深层复制,直接将要克隆的对象序列化,然后反序列化得到的对象就是我们期望的结果。
处理
按照上面的说明,代码如下:
Student类
1 [Serializable] 2 public class Student 3 { 4 [field: NonSerializedAttribute()] 5 public event EventHandler Changed; 6 [NonSerialized] 7 private ExParam param; 8 9 public string ID 10 { 11 get; 12 set; 13 } 14 15 public string Name 16 { 17 get; 18 set; 19 } 20 21 public ClassRoom Room 22 { 23 get; 24 set; 25 } 26 27 public ExParam Param 28 { 29 get 30 { 31 return param; 32 } 33 set 34 { 35 param = value; 36 } 37 } 38 }
ClassRoom类
1 [Serializable] 2 public class ClassRoom 3 { 4 5 public string Name 6 { 7 get; 8 set; 9 } 10 11 public string Address 12 { 13 get; 14 set; 15 } 16 }
ExParam类
1 public class ExParam 2 { 3 public string Name 4 { 5 get; 6 set; 7 } 8 }
Util类
1 public class Util 2 { 3 public static byte[] SerializeObject(object obj) 4 { 5 if (obj == null) 6 return null; 7 8 using (MemoryStream memory = new MemoryStream()) 9 { 10 BinaryFormatter formatter = new BinaryFormatter(); 11 formatter.Serialize(memory, obj); 12 memory.Position = 0; 13 byte[] read = new byte[memory.Length]; 14 memory.Read(read, 0, read.Length); 15 memory.Close(); 16 return read; 17 } 18 } 19 20 21 public static object DeserializeObject(byte[] data) 22 { 23 object obj = null; 24 if (data == null) 25 return obj; 26 27 using (MemoryStream memory = new MemoryStream(data)) 28 { 29 memory.Position = 0; 30 BinaryFormatter formatter = new BinaryFormatter(); 31 obj = formatter.Deserialize(memory); 32 memory.Close(); 33 return obj; 34 } 35 } 36 }
主窗体
1 private void Form1_Load(object sender, EventArgs e) 2 { 3 Student student = new Student() 4 { 5 ID = "201401", 6 Name = "攻城狮", 7 Room = new ClassRoom() 8 { 9 Name = "博客园", 10 Address = "小山村" 11 }, 12 Param = new ExParam() 13 { 14 Name = "程序猿" 15 } 16 }; 17 18 byte[] data = Util.SerializeObject(student); 19 20 Student student1 = Util.DeserializeObject(data) as Student; 21 22 Print(student); 23 Print(student1); 24 } 25 26 private void Print(Student student) 27 { 28 string info =string.Format("{0}{1}{2}{3}" 29 , "hashcode:" + student.GetHashCode().ToString() + " " 30 , student.ID + " " + student.Name + " " 31 , student.Room != null ? student.Room.Name + " " + student.Room.Address + " "+student.Room.GetHashCode().ToString()+" " : "room is null " 32 , student.Param != null ? student.Param.Name : "param is null" 33 ); 34 listPrint.Items.Add(info); 35 }
结论
上述代码输出:
从上述代码输出的结果我们可以看出
- student是被序列化的,student1是用student的序列化的二进制反序列化出来的,两个的hashcode不一样,所以是两个对象。
- 标记了序列化的字段都被序列化了,没标记的序列化字段Param是空的。
- student中的属性Room是引用类型,标记为序列化,student和student1的Room里的值内容一样但是hashcode不一样,所以这Room也是我们期望的两个对象。