BinaryFormatter不反序列化IEnumerable上的对象

本文关键字:对象 IEnumerable 反序列化 BinaryFormatter | 更新日期: 2023-09-27 18:17:32

我有这些接口和类(您可以在这里查看所有可编译的相关代码)。为了描述问题(不可编译代码),我只提供最少的代码:

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来序列化它们,它们(IViz.SelectValues属性上的SelectedValue对象)在文件中序列化。

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

我还试图在IViz.SelectedValues属性上添加set;实现,我还试图将该属性设置为IList而不是IEnumerable。然而,结果是一样的:我的SelectedValue对象没有被反序列化。

任何想法?

BinaryFormatter不反序列化IEnumerable上的对象

我能够通过构造EntityValueGroupViz<BOEntity, BOEntity>的实例,向其添加SelectedValue<BOEntity>(new BOEntity(), "hello")并进行序列化来重现您的问题。

然而,仅仅为了重现这个问题,我不得不:

  • 标记BOEntity[Serializable]

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

  • GroupViz<T, TIn, TOut>中分配selectedValues列表

  • 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写入

对象是由内到外重构的,在反序列化过程中调用方法可能会产生不良的副作用,因为调用的方法可能会引用在调用时尚未反序列化的对象引用。

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
    }
    

    样本小提琴。

  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) { }
    }
    

    如您所见,这个解决方案更简单,因此推荐使用。

相关文章: