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
对象没有被反序列化。
任何想法?
我能够通过构造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>
的迭代实际上涉及到调用它的方法。
那么,如何处理这个呢?有几个可能的解决方法:
在
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 }
样本小提琴。
在字段存在的基类中简单地序列化所选值的列表。
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) { } }
如您所见,这个解决方案更简单,因此推荐使用。