一、序列化的意義
序列化是將物件處理為位元組流以儲存物件或傳輸到記憶體、資料庫或檔案。其主要目的是保存物件的狀態,以便在需要時重新建立物件。相反的過程稱為反序列化。
1.1 序列化的工作方式
此圖顯示序列化的整個過程。
物件被序列化為流。流傳遞的不僅是數據,還包括有關物件類型的信息,如物件的版本、區域性和組件名稱。透過該流,可以將物件儲存在資料庫、檔案或記憶體中。
1.2 用於序列化
透過序列化,開發人員可以保存對象的狀態,並在需要時重新建立該對象,從而提供對象的儲存以及資料交換。透過序列化,開發人員還可以執行類似如下的操作:透過Web 服務將物件傳送到遠端應用程式、將物件從一個網域傳遞到另一個網域、以XML 字串的形式跨防火牆傳遞對象,或跨應用程序維護安全資訊或使用者特定資訊。
1.3 使對象可序列化
若要序列化對象,您需要待序列化的對象、要包含序列化對象的流,以及一個 Formatter。 System.Runtime.Serialization包含序列化和反序列化物件所需的類別。
將 SerializableAttribute 特性應用於一個類型可指示該類型的實例可以序列化。嘗試序列化時,如果類型沒有 SerializableAttribute 特性,將引發SerializationException 異常。
如果不希望類別中的欄位可序列化,請套用 NonSerializedAttribute 特性。如果可序列化類型的欄位包含指標、句柄或其他一些專用於特定環境的資料結構,並且無法在不同的環境中以有意義的方式重建,則可能需要使該欄位不可序列化。
如果已序列化的類別包含對標記為 SerializableAttribute 的其他類別的物件的引用,則也將序列化這些物件。
1.3.1 二元序列化與 XML 序列化
可以使用二元序列化或 XML 序列化。在二進位序列化中,會序列化所有成員(甚至包括那些只讀成員),從而可以提高效能。 XML 序列化提供了可讀性較好的程式碼,並在物件共用和使用方面提供了更大的靈活性,以便實現互通性。
1.3.2 二元序列化
二進位序列化使用二元編碼來產生精簡的序列化,以用於儲存或基於套接字的網路流等。
1.3.3 XML 序列化
XML 序列化將物件的公共欄位和屬性或方法的參數及傳回值序列化為符合特定 XML 架構定義語言 (XSD) 文件的 XML 流。 XML 序列化會產生具有轉換為 XML 的公共屬性和欄位的強類型類別。 System.Xml.Serialization 包含序列化和反序列化 XML 所需的類別。
您可以將特性應用於類別和類別成員,以控制 XmlSerializer 序列化或反序列化類別實例的方式。
1.3.4 SOAP 序列化
XML 序列化也可用來將物件序列化為符合 SOAP 規範的 XML 流。 SOAP 是一種基於 XML 的協議,它是專門為使用 XML 來傳輸過程呼叫而設計的。如同常規的 XML 序列化,特性可用於控制 XML Web services 產生的文字樣式的 SOAP 訊息。
1.3.5 基本序列化和自訂序列化
可以透過兩種方式執行序列化:基本序列化和自訂序列化。基本序列化使用 .NET Framework 來自動序列化物件。
1.3.5.1 基本序列化
基本序列化的唯一要求是物件必須應用 SerializableAttribute 特性。
NonSerializedAttribute 可用來禁止序列化特定欄位。
使用基本序列化時,物件的版本控制可能會產生問題,在這種情況下,自訂序列化可能更合適。基本序列化是執行序列化的最簡單的方法,但對進程提供的控制並不多。
1.3.5.2 自訂序列化
在自訂序列化中,可以準確地指定將序列化哪些物件以及如何完成序列化。類別必須標記為 SerializableAttribute,並實作 ISerializable 介面。
如果希望同樣以自訂方式反序列化對象,則必須使用自訂建構函式。
1.3.6 設計器序列
設計器序列化是一種特殊形式的序列化,它涉及通常與開發工具關聯的物件持久性的種類。設計器序列化是將物件圖轉換為以後可用於復原物件圖的來源檔案的過程。原始檔可以包含程式碼、標記,甚至包含 SQL 表資訊。有關更多信息,請參見Designer Serialization Overview。
二、透過序列化保存物件資料
雖然您可以在設計時將物件的屬性設為預設值,但是,如果該物件被損環,則在運行時輸入的所有值都會遺失。 可以使用序列化在實例之間保持物件數據,從而能夠儲存值並且在下次實例化物件時檢索這些值。
在本演練中,將建立一個簡單的對象,並將該對象的資料儲存到檔案中。然後,當您重新建立物件時將從該文件檢索資料。最後,將修改程式碼以使用 SOAP 格式保持物件。
2.1 使用序列化保存物件
[Serializable] //将类标记为可序列化 public class Coupon : INotifyPropertyChanged { public decimal Amount { get; set; } public float InterestRate { get; set; } public int Term { get; set; } private string _name; public string Name { get { return _name; } set { _name = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Customer")); } } [field: NonSerialized()] //将可序列化的类中的某字段标记为不被序列化 public event PropertyChangedEventHandler PropertyChanged; public Coupon(decimal amount, float interestRate, int term, string name) { Amount = amount; InterestRate = interestRate; Term = term; _name = name; } } static void Main(string[] args) { const string fileName = @"demo1.txt"; var coupon = new Coupon(10000, 0.2f, 1, "反骨仔"); using (var stream = File.Create(fileName)) { var deserializer = new BinaryFormatter(); //二进制格式序列化器 deserializer.Serialize(stream, coupon); //序列化对象到文件中 } }
現在嘗試反序列化,看看與先前 Coupon 物件的值是否一致。
static void Main(string[] args) { const string fileName = @"demo1.txt"; //var coupon = new Coupon(10000, 0.2f, 1, "反骨仔"); //判断该文件是否存在 if (!File.Exists(fileName)) { return; } using (var stream = File.OpenRead(fileName)) { var deserializer = new BinaryFormatter(); //二进制序列化器 var coupon = deserializer.Deserialize(stream) as Coupon; //反序列化 if (coupon == null) { return; } Console.WriteLine($"{nameof(Coupon)}:"); Console.WriteLine($" {nameof(coupon.Amount)}: {coupon.Amount}"); Console.WriteLine($" {nameof(coupon.InterestRate)}: {coupon.InterestRate}%"); Console.WriteLine($" {nameof(coupon.Term)}: {coupon.Term}"); Console.WriteLine($" {nameof(coupon.Name)}: {coupon.Name}"); } Console.Read(); }
2.2 使用 SOAP 格式保存物件
static void Main(string[] args) { const string fileName = @"demo1.txt"; var coupon = new Coupon(10000, 0.2f, 1, "反骨仔"); using (var stream = File.Create(fileName)) { var deserializer = new SoapFormatter(); //Soap 格式化器 deserializer.Serialize(stream, coupon); //序列化 } }
反序列化時也採用 SoapFormatter 即可
反序列化時也採用 SoapFormatter 即可