序列化Reference类时遇c__DisplayClass4未序列化异常求助
问题背景
我在序列化Reference类对象时遇到了异常,提示DataAnalysis.Reference+<>c__DisplayClass4未标记为可序列化。一开始我没给委托添加[Serializable]特性,加上之后问题依然存在。Cacheable和Operation类已经标记了[Serializable],而且在引入Reference类之前,它们的序列化完全正常。我不清楚c__DisplayClass4是什么含义,因为源码太大没法全部贴出,推测问题集中在Reference类上。
原Reference类代码:
using System; using System.Reflection; namespace DataAnalysis { /// <summary> /// Description of Reference. /// </summary> [Serializable] public class Reference { [Serializable] public delegate void ReferenceSetter(Operation op, Cacheable c); [Serializable] public delegate Cacheable ReferenceGetter(Operation op); readonly ReferenceGetter refGetter; readonly ReferenceSetter refSetter; public Reference(ReferenceGetter getter, ReferenceSetter setter) { refGetter = getter; refSetter = setter; } public Reference(FieldInfo operationField) { refGetter = (op => (Cacheable)operationField.GetValue(op)); refSetter = ((op, value) => operationField.SetValue(op, value)); } public Cacheable this[Operation op] { get {return refGetter(op);} set {refSetter(op, value);} } } }
问题根因
那个<>c__DisplayClass4是编译器自动生成的闭包类——当你在第二个构造函数里用lambda表达式捕获外部变量operationField时,编译器会创建一个匿名类来持有这个变量,然后让委托引用这个类的实例。这个自动生成的类默认没有标记[Serializable]特性,所以当你序列化Reference对象时,里面的委托会尝试序列化这个闭包类实例,最终抛出异常。哪怕你给委托本身加了[Serializable],也解决不了这个问题,因为问题出在闭包类上。
解决方案
我采用了taffer的第一种方案:避免在委托中直接捕获外部变量,而是把FieldInfo直接存在类的字段里,在索引器的get/set方法中直接使用它。修改后的代码如下:
public class Reference { public delegate void ReferenceSetter(Operation op, Cacheable c); public delegate Cacheable ReferenceGetter(Operation op); readonly FieldInfo opField; readonly ReferenceGetter refGetter; readonly ReferenceSetter refSetter; public Reference(ReferenceGetter getter, ReferenceSetter setter) { refGetter = getter; refSetter = setter; } public Reference(FieldInfo operationField) { opField = operationField; } public Cacheable this[Operation op] { get { if (opField != null) return (Cacheable)opField.GetValue(op); else return refGetter(op); } set { if (opField != null) opField.SetValue(op, value); else refSetter(op, value); } } }
目前代码还没做优化,最终可能会改成抽象Reference类加两个实现类的方案,但核心原理已经明确:绕过闭包类,直接持有需要的字段信息,不在委托中捕获外部变量,从而避免序列化编译器生成的未标记可序列化的闭包类。
内容的提问来源于stack exchange,提问作者oliver

