BinaryFormatter没有反序列化IEnumerable

时间:2016-11-11 13:49:37

标签: c# serialization

我有这个接口和类(您可以查看here以查看所有相关代码是否可编辑)。我只是提供最少的代码来描述问题(不可编译的代码):

interface IViz<T> : ISerializable {
    IEnumerable<SelectedValue> SelectedValues { get; }
};

[Serializable]
abstract class GroupViz<T, TIn, TOut> : IViz<T> {
    public IEnumerable<SelectedValue> SelectedValues
    {
        get { return selectedValues.Cast<SelectedValue>(); }
    }
}

[Serializable]
public class EntityValueGroupViz<TEntity, TKey> : ValueGroupViz<TEntity, TKey>

在第一个界面(IViz)上,我已声明了一个属性IEnumerable<SelectedValue> SelectedValues,其中存储了每个SelectedValue个对象。

SelectedValue有两个实现(泛型和非泛型):

[Serializable]
public abstract class SelectedValue : ISerializable
{
    public SelectedValue(SerializationInfo info, StreamingContext context)
    {
        Configuration.SerializationTemplatesEnum serializationTemplateEnum = (Configuration.SerializationTemplatesEnum)context.Context;

        foreach (SerializationEntry entry in info)
        {
            switch (serializationTemplateEnum)
            {
                case Configuration.SerializationTemplatesEnum.QUERY:
                    switch (entry.Name)
                    {
                        case "Value":
                            Value = entry.Value;
                            break;

                        case "Operator":
                            Operator = (VizOperatorEnum)entry.Value;
                            break;
                    }
                    break;

                case Configuration.SerializationTemplatesEnum.TEMPLATE:
                    break;

            }

        }

    }
}

[Serializable]
public class SelectedValue<T> : SelectedValue, ISerializable
{
    public SelectedValue(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {

    }
}

我使用BinaryFormatter来序列化它们,并且它们(SelectedValue属性上的IViz.SelectValues个对象)在文件中被序列化。

但是,当我尝试反序列化它们时,它们不会被加载。我在SelectedValue(SerializationInfo info, StreamingContext context)构造函数上添加了一个断点,但没有到达。

我还试图在set;媒体资源上添加IViz.SelectedValues实施,我也尝试将该属性设置为IList而不是{{1} }}。但结果是一样的:我的IEnumerable个对象没有被反序列化。

有什么想法吗?

1 个答案:

答案 0 :(得分:2)

我能够通过构建EntityValueGroupViz<BOEntity, BOEntity>的实例,向其添加SelectedValue<BOEntity>(new BOEntity(), "hello")并序列化来重现您的问题。有关this fiddle的信息,请参见mcve

但是,为了达到重现问题的目的,我不得不:

  • BOEntity标记为[Serializable]

  • 将默认和流式构造函数添加到GroupViz<T, TIn, TOut>ValueGroupViz<T, TIn>

  • 在构造函数内的selectedValues内分配GroupViz<T, TIn, TOut>列表。

  • 将默认构造函数添加到EntityValueGroupViz<TEntity, TKey>

完成这些初步修复后,问题在EntityValueGroupViz<TEntity, TKey>的流构造函数中变得明显:

    protected EntityValueGroupViz(SerializationInfo info, StreamingContext context)
    {
        foreach (SerializationEntry entry in info)
        {
            switch (entry.Name)
            {
                case "SelectedValues":
                    foreach (SelectedValue sv in (IEnumerable<SelectedValue>)entry.Value)
                        this.Value(sv);
                    break;
            }
        }
    }

在调用此对象时,(IEnumerable<SelectedValue>)entry.Value具有空条目。但是,为什么呢? BinaryFormatter是图表序列化程序。它们不是存储在纯树中的对象,而是分配临时对象ID并在遇到它们时进行存储。反序列化对象时,无法保证所有引用的对象先前已反序列化。因此,在调用流式构造函数时,entry.Value中的条目可能尚未填充。作为确认,Microsoft writes

  

从内到外重构对象,并且在反序列化期间调用方法可能会产生不良副作用,因为调用的方法可能引用在调用时尚未反序列化的对象引用。

通过List<T>进行迭代实际上涉及在其上调用方法。

那么,如何应对呢?有几种可能的解决方法:

  1. EntityValueGroupViz<TEntity, TKey>上实施IDeserializationCallback,暂时将entry.Value缓存在流构造函数中,稍后将其添加到IDeserializationCallback.OnDeserialization()中的基类:

    [Serializable]
    public class EntityValueGroupViz<TEntity, TKey> : ValueGroupViz<TEntity, TKey>, IDeserializationCallback
    {
        IEnumerable<SelectedValue> cachedEntry;
    
        // Added necessary default constructor.
        public EntityValueGroupViz() : base() { }
    
        protected EntityValueGroupViz(SerializationInfo info, StreamingContext context) : base(info, context)
        {
            foreach (SerializationEntry entry in info)
            {
                switch (entry.Name)
                {
                    case "SelectedValues":
                        cachedEntry = (IEnumerable<SelectedValue>)entry.Value;
                        break;
                }
            }
        }
    
        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("SelectedValues", SelectedValues);
        }
    
        #region IDeserializationCallback Members
    
        public void OnDeserialization(object sender)
        {
            if (cachedEntry != null)
            {
                foreach (SelectedValue sv in cachedEntry)
                    this.Value(sv);
                cachedEntry = null;
            }
        }
    
        #endregion
    }
    

    示例fiddle

  2. 只需序列化该字段所在的基类中的选定值列表。 BinaryFormatter序列化流是完全类型化的,因​​此selectedValues字段可以存储在那里,即使基类不知道集合中的子类型:

    [Serializable]
    public abstract class GroupViz<T, TIn, TOut> : IViz<T>
    {
        // Added necessary default and streaming constructors
        public GroupViz()
        {
            selectedValues = new List<SelectedValue>();
        }
    
        protected GroupViz(SerializationInfo info, StreamingContext context)
        {
            selectedValues = (IList<SelectedValue>)info.GetValue("SelectedValues", typeof(IList<SelectedValue>));
        }
    
        // Allocated the list
        private IList<SelectedValue> selectedValues;
    
        public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("SelectedValues", selectedValues);
        }
    
        public IEnumerable<SelectedValue> SelectedValues
        {
            get { return selectedValues.Cast<SelectedValue>(); }
        }
    
        public void Value(SelectedValue @value)
        {
            this.AddValue(@value.Value, @value.Operator);
        }
    
        private void AddValue(object @value, object vizOperator)
        {
            SelectedValue<TOut> selectedValue = new SelectedValue<TOut>((TOut)value, vizOperator);
            if (!this.selectedValues.Any(sv => sv.Equals(selectedValue)))
                this.selectedValues.Add(selectedValue);
        }
    }
    
    public abstract class ValueGroupViz<T, TIn> : GroupViz<T, TIn, TIn>
    {
        // Added necessary default and streaming constructors
        public ValueGroupViz() : base() { }
    
        protected ValueGroupViz(SerializationInfo info, StreamingContext context) : base(info, context) { }
    }
    
    [Serializable]
    public class EntityValueGroupViz<TEntity, TKey> : ValueGroupViz<TEntity, TKey>
    {
        // Added necessary default constructor.
        public EntityValueGroupViz() : base() { }
    
        protected EntityValueGroupViz(SerializationInfo info, StreamingContext context) : base(info, context) { }
    }
    

    如您所见,此解决方案更简单,因此建议使用。

    示例fiddle #2