程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> .NET陷阱 一 IDeserializationCallback帶來的問題

.NET陷阱 一 IDeserializationCallback帶來的問題

編輯:關於.NET

代碼中有一個類,其中包含一個字典(Dictionary<Key, Value>),本來想讓前者實現IDeserializationCallback接口,以便在反序列化時根據字典的內容做一些初始化工作,結果循環字典元素的代碼就是不走。費了好大勁才找到原因,先來看有問題的代碼:

using System;   
 using System.Collections.Generic;   
 using System.IO;   
 using System.Runtime.Serialization;   
 using System.Runtime.Serialization.Formatters.Binary;   
        
 namespace DotNetBugs   
 {   
     [Serializable]   
     public class Example : IDeserializationCallback   
     {   
         private Dictionary<string, string> map = new Dictionary<string, string>();   
        
         public Example()   
         {   
             map.Add("one", "1");   
             map.Add("two", "2");   
         }   
        
         public void OnDeserialization(object sender)   
         {   
             Dump();   
         }   
        
         public void Dump()   
         {   
             foreach (var item in map)   
             {   
                 Console.WriteLine(item.Key + " -> " + item.Value);   
             }   
         }   
     }   
        
     public class Starter   
     {   
         public static void Main(string[] args)   
         {   
             using (var stream = new MemoryStream())   
             {   
                 var formatter = new BinaryFormatter();   
                 formatter.Serialize(stream, new Example());   
        
                 stream.Seek(0, SeekOrigin.Begin);   
                 var example = (Example)formatter.Deserialize(stream);   
        
                 Console.WriteLine("after deserialize");   
                 example.Dump();   
        
                 Console.Read();   
             }   
         }   
     }   
 }

你期望控制台有什麼樣的輸出呢,是不是這樣?

one -> 1               |   
two -> 2               | 在第44行反序列化時,Example.OnDeserialization中調用Dump的輸出。   
after deserialize   
one -> 1               |   
two -> 2               | 在第47行調用Dump的輸出

但實際的輸出內容是:

after deserialize   
one -> 1   
two -> 2

為什麼會這樣呢?

來看一下Dictionary<Key, Value>的源代碼(通過.NET Reflector反編譯得到,代碼已經簡化,只顯示與此問題相關的部分 ):

[Serializable]   
 public class Dictionary<TKey, TValue> : ISerializable, IDeserializationCallback   
 {   
     private SerializationInfo m_siInfo;   
            
     protected Dictionary(SerializationInfo info, StreamingContext context)   
     {   
         this.m_siInfo = info;   
     }   
            
     public virtual void GetObjectData(SerializationInfo info, StreamingContext context)   
     {   
         info.AddValue("Version", this.version);   
         info.AddValue("Comparer", this.comparer, typeof(IEqualityComparer<TKey>));   
         info.AddValue("HashSize", (this.buckets == null) ? 0 : this.buckets.Length);   
         if (this.buckets != null)   
         {   
             KeyValuePair<TKey, TValue>[] array = new KeyValuePair<TKey, TValue>[this.Count];   
             this.CopyTo(array, 0);   
             info.AddValue("KeyValuePairs", array, typeof(KeyValuePair<TKey, TValue>[]));   
         }   
     }   
        
     public virtual void OnDeserialization(object sender)   
     {   
         if (this.m_siInfo != null)   
         {   
             int num = this.m_siInfo.GetInt32("Version");   
             int num2 = this.m_siInfo.GetInt32("HashSize");   
             this.comparer = (IEqualityComparer<TKey>)this.m_siInfo.GetValue(   
                   "Comparer", typeof(IEqualityComparer<TKey>));   
             if (num2 != 0)   
             {   
                 this.buckets = new int[num2];   
                 for (int i = 0; i < this.buckets.Length; i++)   
                 {   
                     this.buckets[i] = -1;   
                 }   
                 this.entries = new Entry<TKey, TValue>[num2];   
                 this.freeList = -1;   
                 KeyValuePair<TKey, TValue>[] pairArray = (KeyValuePair<TKey, TValue>[])   
                       this.m_siInfo.GetValue("KeyValuePairs",    
                       typeof(KeyValuePair<TKey, TValue>[]));   
                 if (pairArray == null)   
                 {   
                     ThrowHelper.ThrowSerializationException(   
                           ExceptionResource.Serialization_MissingKeyValuePairs);   
                 }   
                 for (int j = 0; j < pairArray.Length; j++)   
                 {   
                     if (pairArray[j].Key == null)   
                     {   
                         ThrowHelper.ThrowSerializationException(   
                               ExceptionResource.Serialization_NullKey);   
                     }   
                     this.Insert(pairArray[j].Key, pairArray[j].Value, true);   
                 }   
             }   
             else
             {   
                 this.buckets = null;   
             }   
             this.version = num;   
             this.m_siInfo = null;   
         }   
     }   
 }

原來Dictionary<Key, Value>在內部是通過數組的形式將自己的內容序列化到流中的,它也實現了IDeserializationCallback接口,用於在反序列化時重新構建字典。

問題就在這裡——在Example類的對象被反序列化時,對象圖中一共有兩個實現IDeserializationCallback接口的對象,而且從結果來看,Example.map的OnDeserialization方法是在Example類對象之後被調用的,所以Example.OnDeserialization調用時map中還沒有任何內容!

所以要解決這一問題,我們需要將代碼改成下面那樣:

using System;   
 using System.Collections.Generic;   
 using System.IO;   
 using System.Runtime.Serialization;   
 using System.Runtime.Serialization.Formatters.Binary;   
        
 namespace dotNetBugs   
 {   
     [Serializable]   
     public class Example : ISerializable, IDeserializationCallback   
     {   
         [NonSerialized]   
         private Dictionary<string, string> map = new Dictionary<string, string>();   
        
         public Example()   
         {   
             map.Add("one", "1");   
             map.Add("two", "2");   
         }   
        
         private Example(SerializationInfo info, StreamingContext context)   
         {   
             var keys = (string[])info.GetValue("MapKeys", typeof(object));   
             var vals = (string[])info.GetValue("MapVals", typeof(object));   
             map = new Dictionary<string, string>();   
             for (int i = 0; i < keys.Length; ++i)   
             {   
                 map.Add(keys[i], vals[i]);   
             }   
         }   
        
         public void OnDeserialization(object sender)   
         {   
             Dump();   
         }   
        
         public void Dump()   
         {   
             foreach (var item in map)   
             {   
                 Console.WriteLine(item.Key + " -> " + item.Value);   
             }   
         }   
        
         public void GetObjectData(SerializationInfo info, StreamingContext context)   
         {   
             var keys = new string[map.Count];   
             var vals = new string[map.Count];   
             int i = 0;   
             foreach (var item in map)   
             {   
                 keys[i] = item.Key;   
                 vals[i] = item.Value;   
                 ++i;   
             }   
        
             info.AddValue("MapKeys", keys);   
             info.AddValue("MapVals", vals);   
         }   
     }   
        
     public class Starter   
     {   
         public static void Main(string[] args)   
         {   
             using (var stream = new MemoryStream())   
             {   
                 var formatter = new BinaryFormatter();   
                 formatter.Serialize(stream, new Example());   
        
                 stream.Seek(0, SeekOrigin.Begin);   
                 var example = (Example)formatter.Deserialize(stream);   
        
                 Console.WriteLine("after deserialize");   
                 example.Dump();   
        
                 Console.Read();   
             }   
         }   
     }   
 }

這樣,輸出就像預料的一樣了。

總結一下:如果某個類Outer實現了IDeserializationCallback接口,而且OnDeserialization方法中的邏輯依賴於Outer類的某個成員inner,則一定檢查inner是否也實現了IDeserializationCallback接口,如果是就需要特殊處理它的序列化過程。

出處:http://www.cnblogs.com/brucebi/archive/2013/04/01/2993968.html

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved