首页 > 解决方案 > Exception: DataAnalysis.Reference+<>c__DisplayClass4 is not serializable

问题描述

I am trying to serialize objects of class Reference at the end of my program. A serialization exception is thrown, which complains that "DataAnalysis.Reference+<>c__DisplayClass4" is not marked as serializable.

Initially I had the two delegates without the Serializable attribute, so I gave it a try, but it didn't change anything. The classes Cacheable and Operation are already marked as Serializable - and in fact the serialization of the both of them worked perfectly fine before I introduced the Reference class.

I don't even know what c__DisplayClass4 means. So I am sorry, but I don't know what other parts of my 1 megabytes+ source code to post here to help you solve the problem, because in the end I would be posting everything.

As I said, everything worked fine before introducing the Reference class. So I am hoping the problem is somehow localized to it.

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

Edit: I have chosen taffer's first solution (avoid using the FieldInfo inside a delegate):

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

Not polished yet, I will probably finally use an abstract Reference class with two implementations. But the principle becomes clear.

标签: c#serializationreflectiondelegates

解决方案


You get the error because of the way you initialize your fields in the second constructor:

public Reference(FieldInfo operationField)
{
    // operationField is captured in the lambda below, which causes to generate an inner class
    // where operationField will be a field so can be accessed by the method of the lambda body
    refGetter = (op => (Cacheable)operationField.GetValue(op));
    refSetter = ((op, value) => operationField.SetValue(op, value));
}

Solution 1:

Do not capture locals and parameters of the enclosing method in the lambda. The field should rather be a parameter of the delegate.

Solution2:

Implement ISerializable and provide a custom serialization:

void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
    info.AddValue("getter", refGetter);
    info.AddValue("setter", refSetter);
}

// the special constructor needed for deserialization
private Reference(SerializationInfo info, StreamingContext context)
{
    refGetter = (ReferenceGetter)info.GetValue("getter", typeof(ReferenceGetter));
    refSetter = (ReferenceSetter)info.GetValue("setter", typeof(ReferenceSetter));
}

Please note that deserializing delegates of non-static methods can be problematic. Maybe you should check the Delegate.Method property in GetObjectData and throw an exception if the setter or getter is an instance method.


推荐阅读