【问题标题】:DataContract Serialization of IEnumerable<> backed by 'yield return' statements由“yield return”语句支持的 IEnumerable<> 的 DataContract 序列化
【发布时间】:2012-02-29 20:59:24
【问题描述】:

是否可以序列化 IEnumerable 属性,其中值由“yield return”语句支持?如果可能,怎么做?不是,为什么?

每当我尝试这样做时,都会从 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 中运行

【问题讨论】:

标签: c# xml-serialization datacontract


【解决方案1】:

考虑以下顺序:

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

想象一下这个序列被枚举到点 #1 并暂停。即使您能够序列化它,您将如何恢复它? DataContractSerializer 无法神奇地重新打开您的文件。

没有安全的方法来恢复/反序列化 yield 支持的序列,因为这样的序列可以做任何事情。它可以打开一个消息框或格式化您的硬盘。

这就是为什么 C# 语言设计者没有在其迭代器类上公开任何可用于序列化或反序列化的功能。

仅使用反射手动序列化迭代器类上的字段将始终依赖于编译器实现细节。尚未准备好生产。

【讨论】:

  • 在您的示例中,如果枚举被暂停,我希望反序列化结果仅包含一个空白字符串项。如果枚举没有暂停,我希望 ReadToEnd 的输出被序列化/反序列化,不涉及神奇的文件 I/O。此外,您能否详细说明您的声明,即反序列化 yield-backed 序列可能会打开一个消息框或格式化硬盘驱动器?我看不出向 IEnumerable 添加操作如何导致该操作被执行。
  • 在 #1 点有一个打开的文件句柄,在 #1 点之后使用。反序列化的行为不是这里的问题。问题是当我们重新启动序列时会发生什么(MoveNext 到下一个项目)。你认为会发生什么? FileStream 类无法在反序列化后神奇地重新打开自己。没有 point 反序列化无法继续执行的东西。在许多情况下这会非常危险,因此 C# 设计团队不允许这样做。
  • 虽然这在 .NET 4.0 中不起作用,但它似乎在 .NET 4.5+ 中起作用
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-04
  • 2014-10-07
  • 2010-11-07
  • 2011-03-10
  • 2011-04-20
  • 2013-03-16
  • 1970-01-01
相关资源
最近更新 更多