Anonymous methods serialization
-
Tuesday, November 15, 2005 2:46 PMAnonymous methods are really useful to leverage methods such as AppDomain.DoCallBack. Actually, when the anonymous methods does not rely on the context, the produced delegate is serializable. For example, the following code do work
class Program
{
static void Main(string[] args) {
Foo(delegate() { Console.WriteLine("42"); });
}
static void Foo(CrossAppDomainDelegate d) {
MemoryStream stream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, d);
}
}
But this one does not
class Program
{
static void Main(string[] args) {
int x = 42;
Foo(delegate() { Console.WriteLine(x); });
}
static void Foo(CrossAppDomainDelegate d) {
MemoryStream stream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, d);
}
}
and produces a serialization exception.
What would you suggest as a workaround to this situation? I can still come back to the old ways (i.e. creating an helper class dedicated to the sole purpose of crossing the boundary), but this solution defeats the benefits of the anonymous methods.
Joannès
All Replies
-
Sunday, March 12, 2006 12:36 AMModerator
There are a couple of known issues with anonymous delegates and Remoting.
These issues are not bugs, but explict design decisions.
The issue is what do you do with the type that encloses the context and execution?
Should it be derived from MBRO and/or marked [Serializable]?
If so, when?
We decided that we would leave the enclosing type alone for v2.0.
-
Wednesday, March 29, 2006 12:20 PM
Arrrgh. this is incredibly annoying. could you not at least have added the Serializable attribute if the enclosing class had it? this would make it workable for people who did need to serialize the object graph containing anonymous dlegates with automatic classes.
If you want to get fancy you could work out if the vaiables of the class which are referenced which cause the auto generated class are themselves marked as serializable and do that. Heck you could have added a parameter to the Serializable atribute to indicate that autogenerated nested classes were themselves serializable!
This way makes it possible! and if someone marks the enclosing class as non serializable you're fine.
You say it is not a bug it is a design descision - MBRO I get. Justify how *not* marking thge class as serializable is in anyway a good design decision given that, in Binary serialization, there is currently no way to say "ignore the Serializable" attribute and just try anyway.
Either would suffice. You provide neither and ruin an exceptionally useful use case (that you came so close to fixing after the ridiculous "can't serialize a delegate which references a non public method")
-
Friday, October 13, 2006 12:32 PM
Can you please add support for anonymous delegate serialization in 3.0.
It is really ruining the effect of such a nice feature not having the ability to stream it on disk or remote the object.
-
Tuesday, December 18, 2007 3:24 PM
It appears that Microsoft has not provided support for delegate serialization in 3.0. So, also desiring to have this work, I researched how to serialize anonymous delegates myself. Here is an example. It is specific to the Action delegate type, but the same logic could be generalized into a generic type that could wrapper any delegate and make it serializable even if it represents a closure.
Code Block[
Serializable]public
class Modification : ISerializable{
Action<TModel> action;internal Modification(Action<TModel> action)
{
this.action = action;}
internal Modification(SerializationInfo info, StreamingContext context){
// Simply retrieve the action if it is serializable if (info.GetBoolean("isSerializable"))
this.action = (Action<TModel>)info.GetValue("action", typeof(Action<TModel>)); // Otherwise, recreate the action based on its serialized components else{
// Retrieve the serialized method reference MethodInfo method = (MethodInfo)info.GetValue("method", typeof(MethodInfo));// Create an instance of the anonymous delegate class
object target = ReflectionUtil.CreateObject(method.DeclaringType, BindingFlags.NonPublic | BindingFlags.Instance);// Initialize the fields of the anonymous instance
foreach (FieldInfo field in method.DeclaringType.GetFields())field.SetValue(target, info.GetValue(field.Name, field.FieldType));
// Recreate the action delegate
Action<TModel>)Delegate.CreateDelegate(typeof(Action<TModel>), target, method.Name);action = (
}
}
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
// Serialize the action delegate directly if the target is serializable
if (action.Target == null || action.Target.GetType().GetCustomAttributes(typeof(SerializableAttribute), false).Length > 0){
"isSerializable", true);info.AddValue(
info.AddValue(
"action", action);}
// Otherwise, serialize information necessary to recreate the action delegate
else{
"isSerializable", false);info.AddValue(
info.AddValue(
"method", action.Method); foreach (FieldInfo field in action.Method.DeclaringType.GetFields())info.AddValue(field.Name, field.GetValue(action.Target));
}
}
internal void Apply(TModel model)
{
action(model);
}
}
Please note that ReflectionUtil is just a utility class that simplifies using reflection. With a few more lines of code you can easily create the anonymous delegate class instance using standard reflection code. Essentially, this class stores a reference to a delegate and implements ISerializable in order to perform custom serialization. During serialization, it determines if the target of the delegate is serializable. If it is, it just serializes the delegate. If not, it assumes the delegate is a method on a generated class due to an anonymous delegate representing a closure. It then serializes the method reference and the values of the fields, which it then uses to reproduce the delegate during deserialization. Works like a charm.
HTH,
Jamie
-
Friday, June 06, 2008 1:30 PMI'm trying to get something similar to the code above working, but its having none of it...
we get to the end of this code block:// Otherwise, serialize information necessary to recreate the action delegate
else
{
info.AddValue("isSerializable", false);
info.AddValue("method", action.Method);
foreach (FieldInfo field in action.Method.DeclaringType.GetFields())
info.AddValue(field.Name, field.GetValue(action.Target));
}
then from what I can work out - the compiler generated inner class is still trying to be serialized and of course throwing an exception as its not marked as serializable...
Any ideas??
-
Wednesday, June 11, 2008 10:20 AMPlease ignore the above post...
I've sorted it now,
Cheers,
J.

