C# [Serializable]的作用 序列化
定義:序列化 (Serialization)將物件的狀態資訊轉換為可以儲存或傳輸的形式的過程。在序列化期間,物件將其當前狀態寫入到臨時或永續性儲存區。以後,可以通過從儲存區中讀取或反序列化物件的狀態,重新建立該物件(百度百科)
序列化的目的:1、以某種儲存形式使自定義物件持久化;2、將物件從一個地方傳遞到另一個地方。3、使程式更具維護性。
技術:
* 二進位制序列化保持型別保真度,這對於在應用程式的不同調用之間保留物件的狀態很有用。例如,通過將物件序列化到剪貼簿,可在不同的應用程式之間共享物件。您可以將物件序列化到流、磁碟、記憶體和網路等等。遠端處理使用序列化“通過值”在計算機或應用程式域
* XML 序列化僅序列化公共屬性和欄位,且不保持型別保真度。當您要提供或使用資料而不限制使用該資料的應用程式時,這一點是很有用的。由於 XML 是一個開放式標準,因此,對於通過 Web 共享資料而言,這是一個很好的選擇。SOAP 同樣是一個開放式標準,這使它也成為一個頗具吸引力的選擇。(百度百科)
例如,可以序列化一個物件,然後使用 HTTP 通過 Internet 在客戶端和伺服器之間傳輸該物件。反之,反序列化根據流重新構造物件。
實現:在此過程中,先將物件的公共欄位和私有欄位以及類的名稱(包括類所在的程式集)轉換為位元組流,然後再把位元組流寫入資料流。在隨後對物件進行反序列化時,將創建出與原物件完全相同的副本
實質:序列化就是把記憶體中物件以一種可以儲存的形式儲存起來。
原因:將物件的狀態儲存在儲存媒體中以便可以在以後重新創建出完全相同的副本;按值將物件從一個應用程式域傳送至另一個應用程式域。
Serializable特性的作用
該標籤指示一個類可以序列化。 便於在網路中傳輸和儲存
但是像WCF,即使你不去標記,它也會花時間去反射欄位。它已經不在乎SerializableAttribute了。
需要注意的是:
序列化可以制定序列化整個類,或者 類的某些屬性。
總結:
序列化整個類使用Serializable標記,部分屬性不序列化使用NonSerialized
建立本地檔案流儲存資料
FileStream fs = new FileStream(fileFullName, FileMode.Open, FileAccess.ReadWrite)
或本地記憶體流儲存資料
MemoryStream stream = new MemoryStream( );
使用 XmlSerializer類序列化
XmlSerializer xs = new XmlSerializer(obj.GetType());
//序列化成Xml檔案.
xs.Serialize(fs, obj);
使用 BinaryFormatter類序列化
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(fs, obj);
自定義序列化
實現 ISerializable,需要實現 GetObjectData方法以及一個特殊的建構函式
你是想資料持久化嗎?
首先,如果Rectangle是你自定義的類的話,必須保證這個類裡面的所有狀態都是可以儲存的,並且要有[Serializable()]標識.
然後你可以用 XmlSerializer類或者 BinaryFormatter類將其逐一序列化.序列化後。
可以將其序列化成XML存進資料庫也可以序列化成2進位制儲存.
==========================
首先,你想儲存某個類中的資料,那這個類最好是個實體類,
比如
[Serializable]
[XmlRoot("Rectangle")]
public class Rectangle{
//fields here.
...
public Rectangele(){...}
[XmlElement("Width")]
public int Width{get;set;}
[XmlElement("Length")]
public int Length{get;set;}
}
像這樣的類被選擇序列化才比較有意義,它要求必須有一個無引數的建構函式.
經過類似下面的方法序列化後,將獲得一個檔案流
Rectangle rec = new Rectangle{Width = 5,Length = 6};
using (FileStream fs = new FileStream(fileFullName, FileMode.Create, FileAccess.Write, FileShare.Read))
{
XmlSerializer xs = new XmlSerializer(rec.GetType());
//序列化成Xml檔案.
xs.Serialize(fs, rec);
//使用檔案流
...
}
同樣可以序列化成 2進位制檔案流.:
using (FileStream fs = new FileStream(fileFullName, FileMode.Open, FileAccess.ReadWrite))
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(fs, rec);
}
另外,如果你的類用來記錄狀態,比如是否跟遠端主機連線成功,之類的,那麼序列化是沒有意義的.因為當你反序列化的時候,他的環境已經變了,而以前儲存的狀態已經不穩定了.
序列化,是記錄類中所有可序列化的資訊.並可以還原.
另外一個選擇呢,就是隻記錄內容,用內容重新例項化類:
比如
Rectangle rec = new Rectangle{Width = 5,Length = 6};
rec 你只需要在資料庫中記錄Width = 5,Length = 6
在將來,你可以根據 Width,Length的值重新例項化Rectangle類.
至於集合,可類推.
選擇性序列化
類通常包含不應被序列化的欄位。例如,假設某個類用一個成員變數來儲存執行緒 ID。當此類被反序列化時,序列化此類時所儲存的 ID 對應的執行緒可能不再執行,所以對這個值進行序列化沒有意義。可以通過使用 NonSerialized 屬性標記成員變數來防止它們被序列化,如下所示:
[Serializable]
public class MyObject
{
public int n1;
[NonSerialized] public int n2;
public String str;
}
自定義序列化
可以通過在物件上實現 ISerializable 介面來自定義序列化過程。這一功能在反序列化後成員變數的值失效時尤其有用,但是需要為變數提供值以重建物件的完整狀態。要實現 ISerializable,需要實現 GetObjectData方法以及一個特殊的建構函式,在反序列化物件時要用到此建構函式。以下程式碼示例說明了如何在前一部分中提到的 MyObject 類上實現 ISerializable。
[Serializable]
public class MyObject : ISerializable
{
public int n1;
public int n2;
public String str;
public MyObject()
{
}
protected MyObject(SerializationInfo info, StreamingContext context)
{
n1 = info.GetInt32("i");
n2 = info.GetInt32("j");
str = info.GetString("k");
}
public virtual void GetObjectData(SerializationInfo info,
StreamingContext context)
{
info.AddValue("i", n1);
info.AddValue("j", n2);
info.AddValue("k", str);
}
}
在序列化過程中呼叫 GetObjectData 時,需要填充方法呼叫中提供的 SerializationInfo 物件。只需按名稱/值對的形式新增將要序列化的變數。其名稱可以是任何文字。只要已序列化的資料足以在反序列化過程中還原物件,便可以自由選擇新增至 SerializationInfo 的成員變數。如果基物件實現了 ISerializable,則派生類應呼叫其基物件的 GetObjectData 方法。
需要強調的是,將 ISerializable 新增至某個類時,需要同時實現 GetObjectData 以及特殊的建構函式。如果缺少 GetObjectData,編譯器將發出警告。但是,由於無法強制實現建構函式,所以,缺少建構函式時不會發出警告。如果在沒有建構函式的情況下嘗試反序列化某個類,將會出現異常。在消除潛在安全性和版本控制問題等方面,當前設計優於 SetObjectData 方法。例如,如果將 SetObjectData 方法定義為某個介面的一部分,則此方法必須是公共方法,這使得使用者不得不編寫程式碼來防止多次呼叫 SetObjectData 方法。可以想象,如果某個物件正在執行某些操作,而某個惡意應用程式卻呼叫此物件的 SetObjectData 方法,將會引起一些潛在的麻煩。
NonSerialized 屬性標記成員變數來防止它們被序列化
序列化過程的步驟
在格式化程式上呼叫 Serialize 方法時,物件序列化按照以下規則進行:
檢查格式化程式是否有代理選取器。如果有,檢查代理選取器是否處理指定型別的物件。如果選取器處理此物件型別,將在代理選取器上呼叫 ISerializable.GetObjectData。
如果沒有代理選取器或有卻不處理此型別,將檢查是否使用 Serializable 屬性對物件進行標記。如果未標記,將會引發 SerializationException。
如果物件已被正確標記,將檢查物件是否實現了 ISerializable。如果已實現,將在物件上呼叫 GetObjectData。
如果物件未實現 Serializable,將使用預設的序列化策略,對所有未標記為 NonSerialized 的欄位都進行序列化。
/// <summary>
/// 序列化和反序列化輔助類
/// </summary>
public class SerializableHelper
{
public string Serializable(object target)
{
using (MemoryStream stream = new MemoryStream())
{
new BinaryFormatter().Serialize(stream, target);
return Convert.ToBase64String(stream.ToArray());
}
}
public object Derializable(string target)
{
byte[] targetArray = Convert.FromBase64String(target);
using (MemoryStream stream = new MemoryStream(targetArray))
{
return new BinaryFormatter().Deserialize(stream);
}
}
public T Derializable<T>(string target)
{
return (T)Derializable(target);
}
//深拷貝 ,序列化 後反序列化
public T CopyObj<T>(T obj)
{
string target =Serializable(obj);
return Derializable<T>(target);
}
}
1 MemoryStream stream = new MemoryStream() //建立一個流,其後備儲存為記憶體。
2 new BinaryFormatter().Serialize(stream, target) // 以二進位制格式序列化和反序列化物件。
3 Convert.ToBase64String(stream.ToArray()); //轉換為使用 Base-64 位數編碼的相等字串表示
BinaryFormatter 序列化與反序列的物件
序列化: 1 把物件儲存到記憶體流 2把記憶體流轉化為位元組 3把位元組轉化為字串
反序列化 1 把字串轉化為位元組 2 把位元組轉換為記憶體流 3 把記憶體流轉化為物件
參考文獻: