2012-02-29 46 views
4

是否可以序列化一個IEnumerable屬性,其中的值由'yield return'語句支持?如果可能的話,怎麼樣?它不是,爲什麼?DataContract由'yield return'語句支持的IEnumerable <>的序列化

每當我嘗試這樣做時,我都會從DataContractSerializer中收到一個NullReferenceException。舉個例子:

using System.Collections.Generic; 
using System.IO; 
using System.Runtime.Serialization; 

namespace IEnumerableTest 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      DataContractSerializer ser = 
       new DataContractSerializer(typeof(Test)); 

      using (FileStream writer = new FileStream("test.xml", FileMode.Create)) 
      { 
       Test test = new Test(); 
       //NullReferenceException thrown by the next call 
       //if YieldValues is flagged as [DataMember] 
       ser.WriteObject(writer, test); 
      } 
     } 
    } 

    [DataContract(Name = "Test")] 
    public class Test 
    { 
     List<int> values = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8 }; 

     //This property serializes without issue 
     [DataMember] 
     public IEnumerable<int> Values 
     { 
      get 
      { 
       return values; 
      } 
     } 

     //Attempting to serialize this member results in a NullReferenceException 
     [DataMember] 
     public IEnumerable<int> YieldValues 
     { 
      get 
      { 
       foreach (int value in values) 
       { 
        yield return value; 
       } 
      } 
     } 

     public Test() 
     { 

     } 
    } 
} 

異常詳細信息:

System.NullReferenceException was unhandled 
    Message=Object reference not set to an instance of an object. 
    Source=System.Runtime.Serialization 
    StackTrace: 
     at System.Runtime.Serialization.XmlObjectSerializerWriteContext.OnHandleIsReference(XmlWriterDelegator xmlWriter, DataContract contract, Object obj) 
     at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithXsiType(XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle objectTypeHandle, Type objectType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, Type declaredType) 
     at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle) 
     at WriteTestToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , ClassDataContract) 
     at System.Runtime.Serialization.ClassDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context) 
     at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle) 
     at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle) 
     at System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) 
     at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) 
     at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) 
     at System.Runtime.Serialization.XmlObjectSerializer.WriteObject(XmlDictionaryWriter writer, Object graph) 
     at System.Runtime.Serialization.XmlObjectSerializer.WriteObject(Stream stream, Object graph) 
     at IEnumerableTest.Program.Main(String[] args) in F:\Users\Caleb\Documents\Visual Studio 2010\Projects\IEnumerable\Program.cs:line 19 
     at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) 
     at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) 
     at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() 
     at System.Threading.ThreadHelper.ThreadStart_Context(Object state) 
     at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx) 
     at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) 
     at System.Threading.ThreadHelper.ThreadStart() 
    InnerException: 

這是在.NET 4.0,Visual Studio 2010中

+2

[從StackOverflow的答案線索] [1] [1]:http://stackoverflow.com/questions/3294224/c-sharp-serialization-and-the-yield-statement – 2012-02-29 21:03:36

+2

我覺得@PeterKiss是試圖說,[從線程回答](http://stackoverflow.com/questions/3294224/c-sharp-serialization-and-the-yield-statement)。 – 2012-02-29 21:09:33

+0

謝謝,我的帖子出了問題,我無法修復它。 – 2012-02-29 21:11:48

回答

2

內部運行的考慮以下順序:

var file = File.Open("a.txt"); 
yield return ""; 
//#1 
yield return new StreamReader(file).ReadToEnd(); 

想象這個序列被枚舉到點#1並暫停。即使你能夠序列化它,你將如何恢復它? DataContractSerializer不能神奇地重新打開你的文件。

沒有安全的方法來恢復/反序列化yield-backed序列,因爲這樣的序列可以做什麼。它可以打開一個消息框或格式化您的硬盤。

這就是爲什麼C#語言設計者沒有在其迭代器類上公開任何可用於序列化或反序列化的功能的原因。

只需手動使用反射序列化迭代器類上的字段將始終依賴於編譯器實現細節。沒有生產就緒。

+0

在你的例子中,如果枚舉被掛起,我希望反序列化的結果只包含一個空白字符串項。如果枚舉沒有被掛起,我期望ReadToEnd的輸出被序列化/反序列化,不會涉及神奇的文件I/O。另外,您是否可以詳細闡述一下您的聲明:反序列化產量支持序列可以打開消息框或格式化硬盤驅動器?我看不出如何將操作添加到IEnumerable可能會導致執行該操作。 – Caleb 2012-03-05 17:25:37

+0

在#1點有一個文件句柄打開,在點#1之後使用。反序列化的行爲在這裏不是問題。問題是我們重新啓動序列時發生了什麼(MoveNext到下一個項目)。你認爲會發生什麼?在反序列化之後,FileStream類沒有能力神奇地重新打開它自己。沒有_point_反序列化的東西不能繼續執行。在很多情況下這會非常危險,所以C#設計團隊不允許這樣做。 – usr 2012-03-05 18:20:08

+0

雖然這在.NET 4.0中不起作用,但它似乎在.NET 4.5+ – Cocowalla 2016-08-31 21:00:30

相關問題