Click here to Skip to main content
Email Password   helpLost your password?

Introduction

Anonymous methods cannot be serialized in C# (unless they do not reference any stack variables). The most simple solution would be to add the [Serializable] attribute to the anonymous class generated for the method, but since this is (as far as I know) not possible yet, we have to use this "hack".

This is an updated version of Jeremy Thomas' version that can be found here, with the improvement that this also supports nested delegates, and any delegate.

Please note that this code is not very well tested.  Also note that anything you reference inside the anonymous method also gets serialized, including this. This means that, if you serialize a delegate referencing this and then deserialize it again and run it, then this will refer to a new copy of the object.

Using the Code 

formater.Serialize(stream, new SerializeDelegate(myDelegate));

Code

[Serializable]
public class SerializeDelegate : ISerializable
{
    internal SerializeDelegate(Delegate delegate_)
    {
        this.delegate_ = delegate_;
    }

    internal SerializeDelegate(SerializationInfo info, StreamingContext context)
    {
        Type delType = (Type)info.GetValue("delegateType", typeof(Type));

        //If it's a "simple" delegate we just read it straight off
        if (info.GetBoolean("isSerializable"))
            this.delegate_ = (Delegate)info.GetValue("delegate", delType);

        //otherwise, we need to read its anonymous class
        else
        {
            MethodInfo method = (MethodInfo)info.GetValue("method", typeof(MethodInfo));

            AnonymousClassWrapper w = 
                (AnonymousClassWrapper)info.GetValue
			("class", typeof(AnonymousClassWrapper));

            delegate_ = Delegate.CreateDelegate(delType, w.obj, method);
        }
    }

    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("delegateType", delegate_.GetType());

        //If it's an "simple" delegate we can serialize it directly
        if ((delegate_.Target == null ||
            delegate_.Method.DeclaringType
                .GetCustomAttributes(typeof(SerializableAttribute), false).Length > 0) &&
            delegate_ != null)
        {
            info.AddValue("isSerializable", true);
            info.AddValue("delegate", delegate_);
        }

        //otherwise, serialize anonymous class
        else
        {
            info.AddValue("isSerializable", false);
            info.AddValue("method", delegate_.Method);
            info.AddValue("class", 
                new AnonymousClassWrapper
		(delegate_.Method.DeclaringType, delegate_.Target));
        }
    }

    public Delegate Delegate { get { return delegate_; } }

    Delegate delegate_;

    [Serializable]
    class AnonymousClassWrapper : ISerializable
    {
        internal AnonymousClassWrapper(Type bclass, object bobject)
        {
            this.type = bclass;
            this.obj = bobject;
        }

        internal AnonymousClassWrapper(SerializationInfo info, StreamingContext context)
        {
            Type classType = (Type)info.GetValue("classType", typeof(Type));
            obj = Activator.CreateInstance(classType);

            foreach (FieldInfo field in classType.GetFields())
            {
                //If the field is a delegate
                if (typeof(Delegate).IsAssignableFrom(field.FieldType))
                    field.SetValue(obj,
                        ((SerializeDelegate)info.GetValue
				(field.Name, typeof(SerializeDelegate)))
                            .Delegate);
                //If the field is an anonymous class
                else if(!field.FieldType.IsSerializable)
                    field.SetValue(obj,
                        ((AnonymousClassWrapper)info.GetValue
				(field.Name, typeof(AnonymousClassWrapper)))
                            .obj);
                //otherwise
                else
                    field.SetValue(obj, info.GetValue(field.Name, field.FieldType));
            }
        }

        void ISerializable.GetObjectData
		(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("classType", type);

            foreach (FieldInfo field in type.GetFields())
            {
                //See corresponding comments above
                if (typeof(Delegate).IsAssignableFrom(field.FieldType))
                    info.AddValue(field.Name, new SerializeDelegate
					((Delegate)field.GetValue(obj)));
                else if (!field.FieldType.IsSerializable)
                    info.AddValue(field.Name, new AnonymousClassWrapper
				(field.FieldType, field.GetValue(obj)));
                else
                    info.AddValue(field.Name, field.GetValue(obj));
            }
        }

        public Type type; 
        public object obj;
    }
}

History

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralUnable to deserialize
padanfain
7:21 16 Jun '09  
I can't seem to deserialize the delegate.

I am doing it like so:

Meh m = new Meh("one");
Thunk method = new Thunk(m.Run);

BinaryFormatter formatter = new BinaryFormatter();
FileStream fs = new FileStream(@"C:\temp\method.dat", FileMode.Create);

SerializeDelegate sd = new SerializeDelegate(method);

formatter.Serialize(fs, sd);

// read it back in and try and run it
fs.Position = 0;
// this line crashes, something about an exception has been thrown by the target of an invocation
//Thunk funkychicken = (Thunk)formatter.Deserialize(fs);
// this also crashes
SerializeDelegate sd2 = (SerializeDelegate)formatter.Deserialize(fs);
Thunk funkychicken = (Thunk)sd2.Delegate;


What am I doing wrong?
GeneralRe: Unable to deserialize
chot
12:40 19 Aug '09  
It's really hard to tell without more information about the exception, but my guess would be that you reference something which isn't Serializable in your method. You can try pressing CTRL-ALT-E to bring up the exception menu and select thrown on everything, and then run the crashing program again. That will make visual studio break when the actual exception is thrown so you'll have more information. That, or, you can look at the InnerException property of the exception but it's a bit messier.
GeneralA question...
Marc Clifton
2:37 23 Feb '09  
Could you provide an example of how to use solution?

[edit]Ummm, ok, I'm blind. There is one line of "Usage", but it's still not very clear to me. Maybe I'm having a brain dead Monday morning.[/edit]

Thanks!

Marc

Available for consulting and full time employment. Contact me.
Interacx

GeneralSerializing/reconstituting delegates
supercat9
17:11 12 Feb '09  
One of the difficulties with serialization in general is that the meaning of objects within a class may depend strongly upon how they are aliased to other objects within an application. Such classes can only be usefully serialized and reconstituted if they have a means of converting their logical structure into a serializable form.

I'm somewhat curious, then, about the contexts in which serialized delegates would be useful. Would you be anticipating that a program would reconstitute the delegate and then have everything else in the world start using the objects that were reconstituted with the delegate instead of their own objects?
GeneralRe: Serializing/reconstituting delegates
chot
1:27 13 Feb '09  
The way I am using this is basically to perform remote procedure calls of anonymous methods. In essence, I can do things like this: Send(someEndPoint, (remoteObject)=> remoteObject.work());. Though I could also imagine other uses, for example storing sequences of operations might be useful in some places.
GeneralRe: Serializing/reconstituting delegates
supercat9
13:21 13 Feb '09  
I guess the thing that confuses me is how the recipient of such an RPC request is supposed to send back the result. At least when doing things on a single machine, a delegate's "this" object will usually either contain a place for the delegate to store its result, or else contain a reference to such a place. Neither approach would seem usable with a serialized and reconstituted delegate. I suppose the called function could do a name lookup of some sort to determine where to put the result, but that seems awkward to say the least. Is there some better trick?
GeneralRe: Serializing/reconstituting delegates
chot
15:34 13 Feb '09  
Well first of, maybe RPC wasn't quite the right word since actually, in most of my calls I don't expect a result. If you do need to handle a result, you can simply send it back with a new anonymous delegate (this is why it supports nested delegates):
Send(ep1, idOfep1Object, (ep1Object) => 
{
var result = ep1Object.DoSomeWork();
Send(ep2, idOfep2Object, (ep2Object) => ep2Object.HandleResultOfWork(result));
});

And I make sure I never reference the this pointer, but rather supply the object as an argument to the delegate. Also, I need to supply an id to the object, which is used on the receiving end to look up to the object and supply it as an argument to the delegate. In my version of SerializeDelegate, I actually have some code which detects a this references and throws an exception.

Just to clarify; normally in network communication (which is for what I am using this) you send messages, and normally the first byte or string or whatever decides what should be done and after that follows some arguments. This is basically the same thing(a method and arguments) just that I don't specify the protocol explicitly. Also this way makes it a bit easier (at least for me) to think about it, you can see what will happen right there.


Last Updated 12 Feb 2009 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010