再談序列化

目錄:數組

  • 序列化、反序列化
  • 類型序列化的前提函數

  • 格式化器序列化原理this

  • 控制序列化和反序列化spa

 

1、序列化、反序列化3d

字節流序列化是將一個對象轉換成一個字節流的過程。code

字節流反序列化是將一個字節流轉回一個對象的過程。orm

--------序列化----------對象

對象:pblog

List<string> p = new List<string>() { "Sun", "Mon", "Star" };

載體:序列化後的字節流載體  ms繼承

System.IO.MemoryStream ms = new System.IO.MemoryStream();

格式化器:序列化工做的工人  formatter

System.Runtime.Serialization.Formatters.Binary.BinaryFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();

序列化:將 p 進行序列化成字節流 ms

formatter.Serialize(ms, p);

-----反序列化------

ms.Position=0;

formatter.Deserialize(ms);

 

實例應用之一:深拷貝

[Serializable]
    internal class Product:ICloneable { public string Name { get; set; } public int Age { get; set; } public NumberFlag Number { get; set; } public object Clone() { return this.MemberwiseClone(); } public Product DeepClone() { using (System.IO.Stream ms = new System.IO.MemoryStream()) { System.Runtime.Serialization.IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); formatter.Context = new System.Runtime.Serialization.StreamingContext(System.Runtime.Serialization.StreamingContextStates.Clone); formatter.Serialize(ms, this); ms.Position = 0; return formatter.Deserialize(ms) as Product; } } } [Serializable] internal class NumberFlag { public string Num { get; set; } }


首先咱們瞭解淺拷貝,拷貝的對象中的引用類型的值的改變會互相影響:

 Product p1 = new Product()
            {
                Name = "1",
                Age = 1,
                Number = new NumberFlag() { Num="01"}
            };                      

            var p2 = p1.Clone() as Product;

            if (p2 != null)
            {

                p2.Number.Num = "22";

                Console.WriteLine("p1 Number:{0}.",p1.Number.Num);

                Console.WriteLine("p2 Number:{0}.",p2.Number.Num);

                Console.ReadKey();
            }

可是,深拷貝可以解決以上問題:

 Product p1 = new Product()
            {
                Name = "1",
                Age = 1,
                Number = new NumberFlag() { Num="01"}
            };                      

            var p2 = p1.DeepClone();

            if (p2 != null)
            {

                p2.Number.Num = "22";

                Console.WriteLine("p1 Number:{0}.",p1.Number.Num);

                Console.WriteLine("p2 Number:{0}.",p2.Number.Num);

                Console.ReadKey();
            }

 

2、類型序列化的前提

 類型默認是不能夠序列化的,須要加上特性 [Serializable]

SerializableAttribute  這個特性只能應用於:引用類型(class)\值類型(struct)\枚舉類型(enum)\委託類型(delegate).

也不能被子類所繼承。

 [Serializable]
    public class Phone
    {
        
    }

    [Serializable]
    public class iPhone:Phone
    {
        
    }

 

3、格式化器序列化原理

 爲了實現格式化器的工做,FCL封裝了一個:受保護、不可實例化的類--FormatterServices

System.Runtime.Serialization.FormatterServices

 

-----格式化器,序列化-------

一、格式化器調用 FormatterServices  的 GetSerializableMembers 方法,這個方法經過反射,返回當前類的成員數組。

public static MemberInfo[] GetSerializableMembers(Type type, StreamingContext context);

二、調用 GetObjectData 方法,返回的是每一個成員對應的本身的值。

public static object[] GetObjectData(object obj, MemberInfo[] members);

三、格式化器,將程序集標識和類型名稱的全名寫入流中。

四、格式化器遍歷以上咱們的獲得的兩個數組:MemberInfo[] 和 object[] ,獲得成員和成員對應值,並寫入流中。

 

-----格式化器,反序列化--------

一、格式化器讀取程序集名稱和類型名稱,並將程序集標識和類型全名傳遞給方法 GetTypeFromAssembly,返回咱們須要反序列化最終獲得的類型。

public static Type GetTypeFromAssembly(Assembly assem, string name);

二、格式化器,調用GetUninitializedObject方法,對當前類型作一些初始化(不調用構造函數,只是爲成員分配點內存~)

public static object GetUninitializedObject(Type type);

三、也是調用GetSerializableMembers方法,獲取成員 。

四、將流中的數據分配到一個object[]數組中。

五、這樣也是得到了成員數組和對應值的數組。將咱們的新分配的對象、MemberInfo[]、object[],傳入方法,獲取咱們最終的反序列化的類型。

public static object PopulateObjectMembers(object obj, MemberInfo[] members, object[] data);

 

4、控制序列化和反序列化

一、個別不須要序列化的字段,標記爲:[NonSerialized],此標記只能應用於字段,並可以被子類繼承。

 [Serializable]
    public class TotalTime 
    {
        public TotalTime(int hours)
        {
            HoursofDay = hours;
            Minutes = hours * 60;
        }

        public int HoursofDay;

        [NonSerialized]
        public int Minutes;
    }    

 

 那好,咱們進行字節流序列化,沒有問題,正常運行。

TotalTime tt1 = new TotalTime(10);   
            
Console.WriteLine("tt1 Minutes:{0}",tt1.Minutes);

System.IO.MemoryStream ms = new System.IO.MemoryStream();

System.Runtime.Serialization.Formatters.Binary.BinaryFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();

formatter.Serialize(ms, tt1);

ms.Position=0;

var tt2= formatter.Deserialize(ms) as TotalTime;

Console.WriteLine("tt2 Minutes:{0}", tt2.Minutes);

Console.ReadKey();

 

由於咱們序列化時只有Minutes字段應用了NonSerialized ,因此值就沒有序列化到字節流中。

接下來的反序列化,咱們也就得不到Minutes在序列化以前的那個值:600.

怎麼辦?

 [Serializable]
    public class TotalTime 
    {
        public TotalTime(int hours)
        {
            HoursofDay = hours;
            Minutes = hours * 60;
        }

        public int HoursofDay;

        [NonSerialized]
        public int Minutes;

        [System.Runtime.Serialization.OnDeserialized]
        private void OnDeserializedMinutes(System.Runtime.Serialization.StreamingContext context)
        {
            Minutes = HoursofDay * 60;
        }

    }    

System.Runtime.Serialization.OnDeserialized  是在反序列化以後的操做。

 

序列化以前

[System.Runtime.Serialization.OnSerializing]

序列化以後

[System.Runtime.Serialization.OnSerialized]

反序列化以前

[System.Runtime.Serialization.OnDeserializing]

反序列化以後

[System.Runtime.Serialization.OnDeserialized]

已上四個屬性,自定義的方法必須獲取一個流上下文的參數,並返回void,方法的名字咱們能夠自定義。此方法最好是私有,以防外界調用。

 

主要仍是理解格式化器的流程,對此還能夠控制序列化和反序列化的數據,只要咱們在經過流獲取值數組的那一步進行操做:

public static object[] GetObjectData(object obj, MemberInfo[] members);

---------  後續會追加Json.Net 的一些對比 ---------

洗刷,洗刷~~~

相關文章
相關標籤/搜索