我尝试为以下问题创建对象模型。 我需要一个文件夹对象(与目录文件夹相当)。每个文件夹可以包含其他子文件夹以及参数对象(与文件相当)。此外,每个参数都需要知道它所在的文件夹。到目前为止这很容易。所以我实施了以下工作解决方案。
我有一个基础对象,可以继承到文件夹或参数:
[Serializable()]
public class Entry
{
public Func<string> GetPath;
public string Path
{
get
{
if (GetPath == null) return string.Empty;
return GetPath.Invoke();
}
}
}
现在我创建了一个FolderEntry,它继承自Entry并支持通过实现IList&lt;&gt;来添加新的子条目。
[Serializable()]
class FolderEntry : Entry, IList<Entry>
{
private readonly List<Entry> _entries;
public FolderEntry()
{
_entries = new List<Entry>();
}
public string FolderName { get; set; }
private void SetPathDelegate(Entry entry)
{
if (entry.GetPath != null) throw new ArgumentException("entry already assigned");
entry.GetPath = () =>
{
if (GetPath == null || string.IsNullOrEmpty(GetPath.Invoke())) return FolderName;
return GetPath.Invoke() + "|" + FolderName;
};
}
public void Add(Entry item)
{
SetPathDelegate(item);
_entries.Add(item);
}
[...]
}
为了支持撤消/重做功能,我通过添加Serializable-Attribute使所有类可序列化。 到目前为止,此序列化使用以下测试:
var folderA = new FolderEntry();
var folderB = new FolderEntry();
folderA.Add(folderB);
var serializer = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
var memStream = new System.IO.MemoryStream();
serializer.Serialize(memStream, folderA);
现在这是我的问题。此外,还需要每个参数在托管列表中知道其索引。我改变了我的Entry-object,使其具有属性Index和委托GetIndex,其方式与Path和GetPath相同:
[Serializable()]
public class Entry
{
public Func<string> GetPath;
public string Path
{
get
{
if (GetPath == null) return string.Empty;
return GetPath.Invoke();
}
}
public Func<int> GetIndex;
public int Index
{
get
{
if (GetIndex == null) return -1;
return GetIndex.Invoke();
}
}
}
在Folder-object的SetPathDelegate中,我分配了新的委托
private void SetPathDelegate(Entry entry)
{
if (entry.GetPath != null) throw new ArgumentException("entry already assigned");
if (entry.GetIndex != null) throw new ArgumentException("entry already assigned");
entry.GetPath = () =>
{
if (GetPath == null || string.IsNullOrEmpty(GetPath.Invoke())) return FolderName;
return GetPath.Invoke() + "|" + FolderName;
};
entry.GetIndex = () =>
{
return _entries.IndexOf(entry);
};
}
如果我尝试序列化这个,我得到一个预测,我在Assembly ...中的“FolderEntry +&lt;&gt; c__DisplayClass2”没有标记为可序列化。我看不出GetPath和GetIndex之间的明显区别。为了缩小范围,我从
替换了SetPathDelegate中创建的GetIndex委托的内容entry.GetIndex = () =>
{
return _entries.IndexOf(entry);
};
要
entry.GetIndex = () =>
{
return -1;
};
令我惊讶的是,这又是可序列化的。为什么不导致我的GetPath委托有关序列化的任何问题,但我的GetIndex委托呢?
答案 0 :(得分:1)
问题是您分配给GetIndex
的匿名函数。在运行时,会创建一个新类型,该类型未标记为可序列化。
根据this post,您应该为格式化程序设置SurrogateSelector
(有一些警告,详细阅读文章):
formatter.SurrogateSelector = new UnattributedTypeSurrogateSelector();
我在这里粘贴文章中的课程,供将来参考,以便彻底解答。
public class UnattributedTypeSurrogate : ISerializationSurrogate
{
private const BindingFlags publicOrNonPublicInstanceFields =
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
public void GetObjectData(object obj,
SerializationInfo info, StreamingContext context)
{
var type = obj.GetType();
foreach (var field in type.GetFields(publicOrNonPublicInstanceFields))
{
var fieldValue = field.GetValue(obj);
var fieldValueIsNull = fieldValue != null;
if (fieldValueIsNull)
{
var fieldValueRuntimeType = fieldValue.GetType();
info.AddValue(field.Name + "RuntimeType",
fieldValueRuntimeType.AssemblyQualifiedName);
}
info.AddValue(field.Name + "ValueIsNull", fieldValueIsNull);
info.AddValue(field.Name, fieldValue);
}
}
public object SetObjectData(object obj,
SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
{
var type = obj.GetType();
foreach (var field in type.GetFields(publicOrNonPublicInstanceFields))
{
var fieldValueIsSerializable = info.GetBoolean(field.Name + "ValueIsNull");
if (fieldValueIsSerializable)
{
var fieldValueRuntimeType = info.GetString(field.Name + "RuntimeType");
field.SetValue(obj,
info.GetValue(field.Name, Type.GetType(fieldValueRuntimeType)));
}
}
return obj;
}
}
public class UnattributedTypeSurrogateSelector : ISurrogateSelector
{
private readonly SurrogateSelector innerSelector = new SurrogateSelector();
private readonly Type iFormatter = typeof(IFormatter);
public void ChainSelector(ISurrogateSelector selector)
{
innerSelector.ChainSelector(selector);
}
public ISerializationSurrogate GetSurrogate(
Type type, StreamingContext context, out ISurrogateSelector selector)
{
if (!type.IsSerializable)
{
selector = this;
return new UnattributedTypeSurrogate();
}
return innerSelector.GetSurrogate(type, context, out selector);
}
public ISurrogateSelector GetNextSelector()
{
return innerSelector.GetNextSelector();
}
}