標籤:
一、概述
序列化是把對象轉變成流。相反的過程就是還原序列化。
哪些場合用到這項技術呢?
1. 把對象儲存到本地,下次運行程式時恢複這個對象。
2. 把對象傳送到網路的另一台終端上,然後在此終端還原這個對象。
3. 複製系統的粘帖板中,然後用快速鍵Ctrl+V恢複這個對象。
常用的序列化流有Binary(二進位流),XML,SOAP。
二、序列化和還原序列化使用案例:
這裡我們把序列化和還原序列化以功能類的形式展現:
public class Serializer { //將類型序列化為字串 public static string Serialize<T>(T t) where T : class { using(MemoryStream stream=new MemoryStream()) { BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(stream, t); return System.Text.Encoding.UTF8.GetString(stream.ToArray()); } } //將類型序列化為檔案 public static void SerializeToFile<T>(T t, string path, string fullName) where T : class { if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } string fullPath = string.Format(@"{0}\{1}", path, fullName); using (FileStream stream = new FileStream(fullPath,FileMode.OpenOrCreate)) { BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(stream, t); stream.Flush(); } } //將類型序列化為檔案 public static void SerializeToFileByXml<T>(T t, string path, string fullName) where T : class { if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } string fullPath = string.Format(@"{0}\{1}", path, fullName); using (FileStream stream = new FileStream(fullPath, FileMode.OpenOrCreate)) { XmlSerializer formatter = new XmlSerializer(typeof(T)); formatter.Serialize(stream, t); stream.Flush(); } } //將字串還原序列化為類型 public static TResult Deserialize<TResult>(string s) where TResult : class { byte[] bs = System.Text.Encoding.UTF8.GetBytes(s); using (MemoryStream stream = new MemoryStream(bs)) { BinaryFormatter formatter = new BinaryFormatter(); return formatter.Deserialize(stream) as TResult; } } //將檔案還原序列化為類型 public static TResult DeserializeFromFile<TResult>(string path) where TResult : class { using (FileStream stream = new FileStream(path,FileMode.Open)) { BinaryFormatter formatter = new BinaryFormatter(); return formatter.Deserialize(stream) as TResult; } } //將xml檔案還原序列化為類型 public static TResult DeserializeFromFileByXml<TResult>(string path) where TResult : class { using (FileStream stream = new FileStream(path, FileMode.Open)) { XmlSerializer formatter = new XmlSerializer(typeof(TResult)); ; return formatter.Deserialize(stream) as TResult; } } }
上面案例中的方法是以泛型方法實現的,其中附加了泛型約束,保證泛型安全。
序列化功能類有了下面我們建一個Book對象,用它來測試我們的功能類。
[Serializable] public class Book { [NonSerialized] private string _bookPwd; [field: NonSerialized] public event EventHandler NameChanged; private string _bookName; private string _bookID; public ArrayList alBookReader; public string _bookPrice; public Book() { alBookReader = new ArrayList(); } public string BookName { get { return _bookName; } set { if (NameChanged != null) { NameChanged(this, null); } _bookName = value; } } public void BookPwd(string pwd) { _bookPwd=pwd; } public string BookID { get { return _bookID; } set { _bookID = value; } } public void SetBookPrice(string price) { _bookPrice = price; } [OnDeserializedAttribute] public void changeName(StreamingContext context) { this.BookName = "C#深入淺出"; } public void Write() { Console.WriteLine("Book ID:" + BookID); Console.WriteLine("Book Name:" + BookName); Console.WriteLine("Book Password:" + _bookPwd); Console.WriteLine("Book Price:" + _bookPrice); Console.WriteLine("Book Reader:"); for (int i = 0; i < alBookReader.Count; i++) { Console.WriteLine(alBookReader[i]); } } }
關鍵介紹:
1.[Serializable]特性定義該類型可以被序列化;
2.[NonSerialized]定義某個屬性不被序列化,即:內部成員被NonSerialized禁止序列化特性標記;
3.[field: NonSerialized]定義事件不被序列化;
4.[OnDeserializedAttribute]當它應用於某個方法時,會指定對象被還原序列化後立即執行此方法。
5.[OnDeserializingAttribute]當它應用於某個方法時,會指定對象被還原序列化時立即執行此方法。
6.[OnSerializedAttribute]如果將對象圖應用於某個方法時,會指定在序列化該對象圖後是否調用此方法。
7.[OnSerializingAttribute]當它應用於某個方法時,會指定在對象序列化前調用此方法。、
我們用控制台程式實現序列化和還原序列化:
static void Main(string[] args) { string path = "c:\\Test\\"; Book book = new Book(); book.NameChanged += new EventHandler(make_NameChanged); book.BookID = "2001"; book.alBookReader.Add("Abel"); book.alBookReader.Add("Tomson"); book.BookName = "敏捷無敵"; book.BookPwd("*****"); book.SetBookPrice("102.00"); //對象序列化 Serializer.SerializeToFileByXml<Book>(book, path, "book.txt"); //對象還原序列化 Book anothorbookserialize = new Book(); anothorbookserialize = Serializer.DeserializeFromFileByXml<Book>(path + "book.txt"); anothorbookserialize.Write(); Console.ReadKey(); } static void make_NameChanged(object sender, EventArgs e) { Console.WriteLine("Name Changed"); }
我們的案例是調用XML序列化流檔案形式,序列化執行後的檔案如下:
<?xml version="1.0"?>
<Book xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<alBookReader>
<anyType xsi:type="xsd:string">Abel</anyType>
<anyType xsi:type="xsd:string">Tomson</anyType>
</alBookReader>
<_bookPrice>102.00</_bookPrice>
<BookName>敏捷無敵</BookName>
<BookID>2001</BookID>
</Book>
還原序列化輸出如下:
Name Changed
Book ID:2001
Book Name:敏捷無敵
Book Password:
Book Price:102.00
Book Reader:
Abel
Tomson
結果分析:
[NonSerialized]Book Password屬性在序列化XML檔案中沒有出現Book Password。
[field: NonSerialized]NameChanged事件,在序列化XML檔案中沒有出現NameChanged。
[OnDeserializedAttribute]changeName()方法,執行還原序列化後沒有立即執行changeName()方法,Book Name名稱沒有改變,需要通過Binary流形式才能成功執行方法。
//序列化 Serializer.SerializeToFile<Book>(book, path, "book.txt"); //還原序列化 anothorbookserialize = Serializer.DeserializeFromFile<Book>(path + "book.txt");
我們以XML流為例是為了更好的理解序列化和還原序列化的執行過程,實際應用中多數以Binary流形式實現序列化和還原序列化較多。
三、繼承ISerializable介面更靈活的控制序列化過程:
當以上Serializable特性無法滿足複雜的序列化過程時就需要實現ISerializable介面了。
以下是格式化器的工作流程:如果格式化器在序列化一個對象的時候,發現對象實現了ISerializable介面,那他會忽略類所有序列化特性,轉而調用GetObjectData方法的一個SerializationInfo對象,方法內部負責該對象屬性的添加。還原序列化時調用該對象受保護帶參數構造方法中的一個SerializationInfo對象,方法內部對象屬性賦值。
下面我們實現ISerializable介面的子類型應負責父類型的序列化為例:
1.父類同樣實現了ISerializable介面
[Serializable] public class Person:ISerializable { public string Name{get;set;} public Person() { } protected Person(SerializationInfo info,StreamingContext context) { Name = info.GetString("Name"); } public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("Name",Name); } } [Serializable] public class Employee: Person , ISerializable { public int Salary{get;set;}
public Employee() { } protected Employee(SerializationInfo info, StreamingContext context) { Salary = info.GetInt32("Salary"); Name = info.GetString("Name"); } public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info,context); info.AddValue("Salary", Salary); } }
注意:Employee中的GetObjectData方法覆蓋了基類Person中的虛方法GetObjectData。
Employee employee = new Employee() { Name = "Abel", Salary=1220 };BinarySerializer.SerializeToFile<Employee>(employee, strFile, "employee.txt");employee = BinarySerializer.DeserializeFromFile<Employee>("c:\\Test\\employee.txt");Console.WriteLine(employee.Name);Console.WriteLine(employee.Salary);
2.若父類沒有實現了ISerializable介面如何處理呢?
我們要實現繼承ISerializable介面的Employee類的一個父類Person,Person沒有實現ISerializable介面,序列化器沒有預設去處理Person對象,只能由我們自己去做。
下面我們用具體執行個體實現:
[Serializable] public class Person { public string Name{get;set;} } [Serializable] public class Employee: Person , ISerializable { public int Salary{get;set;} public Employee() { } protected Employee(SerializationInfo info, StreamingContext context) { Salary = info.GetInt32("Salary"); Name = info.GetString("Name"); } public void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("Name", Name); info.AddValue("Salary", Salary); } }
在這此序列化學習中我們用到了事件、泛型和流檔案的處理知識,通過序列化我們可以實現本地載入,遠程還原對象。
得到了一個Serializer工具類,該工具類封裝了序列化和還原序列化的過程。
C# 序列化理解 2(轉)