我有这个接口和类(您可以查看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
个对象没有被反序列化。
有什么想法吗?
答案 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>
进行迭代实际上涉及在其上调用方法。
那么,如何应对呢?有几种可能的解决方法:
在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。
只需序列化该字段所在的基类中的选定值列表。 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。