C# 70-536 – Chapter 5
「序列化(Serialization)」:將物件狀態轉換為可保存或可傳輸格式的處理序。序列化的反面是還原序列化,它可以將資料流轉換成物件。
BinaryFormatter:最有效率的序列化,只能讓.NET Framework的程式讀取。
SoapFormatter:以SOAP 格式序列化和還原序列化物件,需要額外加入參考(System.Runtime.Serialization.Formatters.Soap.dll)。
XML 序列化:以XML格式進行序列化,和各種平台相容性最高,但只能序列化公開資料(Public Data),且不能序列化圖形物件。
使用BinaryFormatter進行序列化:
string fPath = @"D:\serial.data";
FileStream fs = new FileStream(fPath, FileMode.OpenOrCreate);
BinaryFormatter bf = new BinaryFormatter();
//Binary序列化
bf.Serialize(fs, DateTime.Now);
fs.Position = 0; //重讀
//反序列化
DateTime dt = (DateTime)bf.Deserialize(fs);
fs.Close();
if (dt != null)
Console.WriteLine(dt.ToLongTimeString());
建立可被BinaryFormatter序列化的類別:
使用SerializableAttribute,不需要序列化的成員則註記NonSerializedAttribute,例:暫存或是計算出來的值,可在還原序列化時再處理。可使用IDeserializationCallback,實作IDeserializationCallback.OnDeserialization,或是使用OnDeserializedAttribute(Soap序列化不支援事件屬性)。
**序列化會有版本相容性問題,所以要特別注意
[Serializable]
class ShoppingCartItem
{
#region prop
private int _productId;
public int ProductId
{
get { return _productId; }
set { _productId = value; }
}
private decimal _price;
public decimal Price
{
get { return _price; }
set { _price = value; }
}
private int _quantity;
public int Quantity
{
get { return _quantity; }
set { _quantity = value; }
}
[NonSerialized] //不需序列化
private decimal _total;
public decimal Total
{
get { return _total; }
}
[OptionalField] //在序列化時可忽略的欄位(相容性處理)
private bool _taxable;
public bool Taxable
{
get { return _taxable; }
set { _taxable = value; }
}
#endregion
public ShoppingCartItem(int productId, decimal price, int quantity)
{
_productId = productId;
_price = price;
_quantity = quantity;
_total = price * quantity;
}
}
還原處理:
//二擇一
//實作IDeserializationCallback
void IDeserializationCallback.OnDeserialization(Object sender)
{
_total = _price * _quantity;
}
//使用OnDeserializedAttribute
[OnDeserializedAttribute]
internal void OnDeserialization(StreamingContext context)
{
_total = _price * _quantity;
}
建立可被XML序列化的類別:
1.類別必需為public
2.要序列化的成員也必需為public
3.沒有參數的建構式
若要自訂XML序列化,則需實作IXmlSerializable。
[XmlRoot("CartItem")]
public class ShoppingCartItem : IXmlSerializable
{
#region prop
private int _productId;
[System.Xml.Serialization.XmlAttribute]
public int ProductId
{
get { return _productId; }
set { _productId = value; }
}
private decimal _price;
public decimal Price
{
get { return _price; }
set { _price = value; }
}
private int _quantity;
public int Quantity
{
get { return _quantity; }
set { _quantity = value; }
}
private decimal _total;
[XmlIgnore]
public decimal Total
{
get { return _total; }
}
#endregion
//xml序列化一定要一個不帶參數的建構式
public ShoppingCartItem()
{
}
public ShoppingCartItem(int productId, decimal price, int quantity)
{
_productId = productId;
_price = price;
_quantity = quantity;
_total = price * quantity;
}
}
自訂XML序列化:
**GetSchema可以不必實作。
#region IXmlSerializable 自訂序列化
System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema()
{
return null;
}
void IXmlSerializable.ReadXml(XmlReader reader)
{
XmlDocument doc = new XmlDocument();
doc.Load(reader);
_price = Convert.ToDecimal(doc.SelectSingleNode("CartItem/Price").InnerText);
_quantity = Convert.ToInt32(doc.SelectSingleNode("CartItem/Quantity").InnerText);
_productId =Convert.ToInt32( doc.DocumentElement.Attributes["ProductId"].Value);
_total = _price * _quantity;
}
void IXmlSerializable.WriteXml(XmlWriter writer)
{
writer.WriteStartAttribute("ProductId"); //Attribute "Name"
writer.WriteString(_productId.ToString()); //Attribute Value
writer.WriteEndAttribute();
writer.WriteStartElement("Price");
writer.WriteString(_price.ToString());
writer.WriteEndElement();
writer.WriteStartElement("Quantity");
writer.WriteString(_quantity.ToString());
writer.WriteEndElement();
}
#endregion
自訂序列化:實作ISerializable,必須實作GetObjectData方法,及還原序列化時使用的特殊建構函式。
StreamingContext:提供序列化目的端的相關資訊
SerializationInfo:執行序列化及反序列化的動作,實作IFormatterConverter。
//for deserialization
protected ShoppingCartItem(SerializationInfo info, StreamingContext context)
{
}
#region ISerializable 成員
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
}
#endregion
序列化及反序列化順序: