2012-02-29 45 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

相关问题