DataContract Сериализация IEnumerable <>, подкрепленная инструкциями yield return - PullRequest
4 голосов
/ 01 марта 2012

Можно ли сериализовать свойство IEnumerable, в котором значения поддерживаются операторами yield return? Если это возможно, то как? Это не, почему?

Я получаю исключение NullReferenceException от DataContractSerializer всякий раз, когда я пытаюсь это сделать. Пример:

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

1 Ответ

2 голосов
/ 01 марта 2012

Рассмотрим следующую последовательность:

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

Представьте, что эта последовательность перечислена в точку # 1 и приостановлена.Даже если бы вы смогли сериализовать его, как бы вы его восстановили?DataContractSerializer не может волшебным образом открыть ваш файл.

Не существует безопасного способа восстановить / десериализовать последовательность с доходностью, потому что такая последовательность может сделать что угодно .Он может открыть окно сообщения или отформатировать ваш жесткий диск.

Вот почему разработчики языка C # не предоставляют никаких функциональных возможностей в своих классах итераторов, которые можно использовать для их сериализации или десериализации.Простая ручная сериализация полей в классе итератора с использованием отражения всегда будет зависеть от деталей реализации компилятора.Не готов к производству.

...