標籤:view create .com img back 繼承 為我 opened pen
建議56:使用繼承ISerializable介面更靈活地控制序列化過程
介面ISerializable的意義在於,如果特性Serializable,以及與其像配套的OnDeserializedAttribute、OnDeserializingAttribute、OnSerializedAttribute、OnSerializingAttribute、NoSerializable等特性不能完全滿足自訂序列化的要求,那就需要繼承ISerializable了。
以下是格式化器的工作流程:如果格式化器在序列化一個對象的時候,發現對象繼承了ISerializable介面,那它就會忽略掉類型所有的序列化特性,轉而調用類型的GetObjectData方法來構造一個SerializationInfo對象,方法內部負責向這個對象添加所有需要序列化的欄位(“添加”這個詞可能不太恰當,因為我們在添加前可以隨意處置這個欄位)。以建議55中的例子為例,如果要為ChineseName構造對應的值,在類繼承ISerializable介面的情況下,應該這樣去實現:
class Program { static void Main() { Person liming = new Person() { FirstName = "Ming", LastName = "Li" }; BinarySerializer.SerializeToFile(liming, @"c:\", "person.txt"); Person p = BinarySerializer.DeserializeFromFile<Person>(@"c:\person.txt"); Console.WriteLine(p.FirstName); Console.WriteLine(p.LastName); Console.WriteLine(p.ChineseName); } } [Serializable] public class Person : ISerializable { public string FirstName; public string LastName; public string ChineseName; public Person() { } protected Person(SerializationInfo info, StreamingContext context) { FirstName = info.GetString("FirstName"); LastName = info.GetString("LastName"); ChineseName = string.Format("{0} {1}", LastName, FirstName); } void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("FirstName", FirstName); info.AddValue("LastName", LastName); } }
序列化工具類:
public class BinarySerializer { //將類型序列化為字串 public static string Serialize<T>(T t) { 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) { if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } string fullPath = Path.Combine(path, fullName); using (FileStream stream = new FileStream(fullPath, FileMode.OpenOrCreate)) { BinaryFormatter formatter = new BinaryFormatter(); 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; } } }View Code
我們在方法GetObjectData中處理序列化,然後在一個帶參數的構造方法中處理還原序列化。雖然在介面中沒有地方指出需要這樣一個構造器,但這確實是需要的,除非我們序列化後不再打算把它還原序列化回來。
這個例子不能表現出ISerializable介面比Serializable特性的優勢,見下面的例子:
將Person序列化,然後在還原序列化中將其變為另一個對象:PersonAnother類型對象。要實現這個功能,需要Person和PersonAnother都實現ISerializable介面,原來其實很簡單,就是在Person類的GetObjectData方法中處理序列化,在PersonAnother的受保護構造方法中還原序列化。
class Program { static void Main() { Person liming = new Person() { FirstName = "Ming", LastName = "Li" }; BinarySerializer.SerializeToFile(liming, @"c:\", "person.txt"); PersonAnother p = BinarySerializer.DeserializeFromFile<PersonAnother>(@"c:\person.txt"); Console.WriteLine(p.Name); } } [Serializable] class PersonAnother : ISerializable { public string Name { get; set; } protected PersonAnother(SerializationInfo info, StreamingContext context) { Name = info.GetString("Name"); } void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { } } [Serializable] public class Person : ISerializable { public string FirstName; public string LastName; public string ChineseName; public Person() { } protected Person(SerializationInfo info, StreamingContext context) { } void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { info.SetType(typeof(PersonAnother)); info.AddValue("Name", string.Format("{0} {1}", LastName, FirstName)); } }
在Person類型的GetObjectData方法中,有句代碼非常重要:
info.SetType(typeof(PersonAnother));
它負責告訴序列化器:我要被還原序列化為PersonAnother。而類型PersonAnother則很簡單,它甚至都不需要知道誰會被還原序列化成它,它不需要做任何特殊處理。
ISerializable介面這個特性很重要,如果運用得當,在版本升級中,它能處理類型因為欄位變化而帶來的問題。
轉自:《編寫高品質代碼改善C#程式的157個建議》陸敏技
【轉】編寫高品質代碼改善C#程式的157個建議——建議56:使用繼承ISerializable介面更靈活地控制序列化過程