2

How can I serialize an object where the object has a Delegate created from an Action such that I can deserialize the object and the Delegate is still subscribed?

Here is the class code:

[Serializable]
public class TestClass
{
    public event EventHandler<EventArgs> refresh;
    public void AddHandlerForAction()
    {
        var methodInfo = this.GetType().GetMethod("RefreshMethodNoParamaters");
        Action action = (Action)Delegate.CreateDelegate(typeof(Action), this, methodInfo);

        var eventInfo = this.GetType().GetEvent("refresh");

        var handlerType = eventInfo.EventHandlerType;
        var eventParams = handlerType.GetMethod("Invoke").GetParameters();

        var parameters = eventParams.Select(p => Expression.Parameter(p.ParameterType, "x"));
        var body = Expression.Call(Expression.Constant(action), action.GetType().GetMethod("Invoke"));
        var lambda = Expression.Lambda(body, parameters.ToArray());
        Delegate d = Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false);

        eventInfo.AddEventHandler(this, d);
    }
    public virtual object Clone()
    {
        Type type = this.GetType();
        if (!type.IsSerializable)
        {
            throw new ArgumentException("The type must be serializable.", "source");
        }

        if (Object.ReferenceEquals(this, null))
        {
            return this.GetType().GetMethod("GetDefaultGeneric").MakeGenericMethod(type).Invoke(this, null);
        }
        using (MemoryStream stream = new MemoryStream())
        {
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream, this);
            stream.Position = 0;
            var clone = Convert.ChangeType(formatter.Deserialize(stream), type);
            return clone;
        }
    }
    public void RaiseEvent()
    {
        EventHandler<EventArgs> eventHandler = refresh;
        if (eventHandler != null)
        {
            eventHandler(this, new EventArgs());
        }
    }
    public void RefreshMethodNoParamaters()
    {

    }
}

Here is the code that performs an exception:

var testClass = new TestClass();
testClass.AddHandlerForAction();
testClass.RaiseEvent();

var clone = testClass.Clone();

The exception occurs at this line of code:

formatter.Serialize(stream, this);

Here is the exception:

An unhandled exception of type 'System.Runtime.Serialization.SerializationException' occurred in mscorlib.dll

Additional information: Cannot serialize delegates over unmanaged function pointers, dynamic methods or methods outside the delegate creator's assembly.

The objective of the code is to subscribe to an Event where the method that is called when the Event is raised has no parameters.

Simon
  • 7,991
  • 21
  • 83
  • 163
  • Avoid doing that it will come back to byte you: http://stackoverflow.com/a/40780504/66372. This answer explains the problem very well and has a recommendation for an alternative: http://stackoverflow.com/a/1133465/66372. – eglasius Nov 24 '16 at 10:09

1 Answers1

3

If you just want to serialize that class you can use [NonSerialized] attribute. But you will need to use attribute target (field):

[field: NonSerialized]
public event EventHandler<EventArgs> refresh;

Here is more about attribute specification

EDIT:

if you need to keep the attached event (and this will work for your example, but not for the long-term storage) you can populate the event handler in your clone function:

public virtual object Clone()
{
    //...
    var clone = Convert.ChangeType(formatter.Deserialize(stream), type);

    (clone as TestClass).refresh = this.refresh;

    return clone;    
}

But for my preference it is a very hacky way...

JleruOHeP
  • 10,106
  • 3
  • 45
  • 71