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 (info.GetBoolean("isSerializable"))
this.delegate_ = (Delegate)info.GetValue("delegate", delType);
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 ((delegate_.Target == null ||
delegate_.Method.DeclaringType
.GetCustomAttributes(typeof(SerializableAttribute), false).Length > 0) &&
delegate_ != null)
{
info.AddValue("isSerializable", true);
info.AddValue("delegate", delegate_);
}
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 (typeof(Delegate).IsAssignableFrom(field.FieldType))
field.SetValue(obj,
((SerializeDelegate)info.GetValue
(field.Name, typeof(SerializeDelegate)))
.Delegate);
else if(!field.FieldType.IsSerializable)
field.SetValue(obj,
((AnonymousClassWrapper)info.GetValue
(field.Name, typeof(AnonymousClassWrapper)))
.obj);
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())
{
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
- 12th February, 2009: Initial post