閱讀目錄
介紹
序列化是將對象狀態轉換為可保持或傳輸的形式的過程。序列化的補集是反序列化,后者將流轉換為對象。這兩個過程一起保證數據易於存儲和傳輸。
.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也是我們期望的兩個對象。