Serializar delegates anónimos en C #

Estoy tratando de determinar qué problemas podrían causar utilizando el siguiente sustituto de serialización para permitir la serialización de funciones anónimas / delegate / lambdas.

// see http://msdn.microsoft.com/msdnmag/issues/02/09/net/#S3 class NonSerializableSurrogate : ISerializationSurrogate { public void GetObjectData(object obj, SerializationInfo info, StreamingContext context) { foreach (FieldInfo f in obj.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) info.AddValue(f.Name, f.GetValue(obj)); } public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) { foreach (FieldInfo f in obj.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) f.SetValue(obj, info.GetValue(f.Name, f.FieldType)); return obj; } } 

Listado 1 adaptado de la Demostración de conteo

El problema principal en el que puedo pensar que podría ser un problema es que la clase anónima es un detalle interno del comstackdor y no se garantiza que su estructura permanezca constante entre las revisiones de .NET Framework. Estoy bastante seguro de que este es el caso basado en mi investigación sobre el problema similar con los iteradores.

Fondo

Estoy investigando la serialización de funciones anónimas. Esperaba que esto no funcionara, pero descubrí que sí funcionaba en algunos casos. Mientras la lambda no * force al comstackdor a generar una clase anónima, todo funciona bien.

Se lanza una SerializationException si el comstackdor requiere una clase generada para implementar la función anónima. Esto se debe a que la clase generada por el comstackdor no está marcada como serializable.

Ejemplo

 namespace Example { [Serializable] class Other { public int Value; } [Serializable] class Program { static void Main(string[] args) { MemoryStream m = new MemoryStream(); BinaryFormatter f = new BinaryFormatter(); // Example 1 Func succeeds = () => 5; f.Serialize(m, succeeds); // Example 2 Other o = new Other(); Func fails = () => o.Value; f.Serialize(m, fails); // throws SerializationException - Type 'Example.Program+c__DisplayClass3' in Assembly 'Example, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable. } } 

Listado 2

Esto es similar al problema de tratar de serializar iteradores y encontré el siguiente código en una búsqueda previa (ver conteo de conteo ) Usando el código del Listado 1 y un ISurrogateSelector, pude serializar y deserializar con éxito el segundo ejemplo de falla.

Objetivo

Tengo un sistema que está expuesto a través de un servicio web. El sistema tiene un estado complejo pero pequeño (muchos objetos, no muchas propiedades por objeto). El estado se guarda en el caché ASP.NET, pero también se serializa en un BLOB en SQL en caso de caducidad del caché. Algunos objetos necesitan ejecutar “eventos” arbitrarios al llegar a alguna condición. Por lo tanto, tienen propiedades que aceptan objetos Action / Func. Ejemplo de ejemplo:

  class Command { public Command(Action action, Func condition); } 

En algún otro lugar

  void DoSomethingWithThing(Thing thing) { state = Store.GetCurrentState(); Command cmd = new Command(() => thing.Foo(), () => thing.IsReady()) state.Add(cmd); Store.Save(state); } 

¿Has visto esta publicación que escribí como continuación de CountingDemo: http://dotnet.agilekiwi.com/blog/2007/12/update-on-persistent-iterators.html ? Desafortunadamente, Microsoft ha confirmado que probablemente cambiarán los detalles del comstackdor (un día), de una manera que probablemente cause problemas. (Por ejemplo, f / cuando actualice al comstackdor nuevo, no podrá deserializar las cosas que guardó con el comstackdor anterior (actual)).

Algunos objetos necesitan ejecutar “eventos” arbitrarios que scopen alguna condición.

¿Qué tan arbitrarios son estos eventos? ¿Pueden ser contados, asignados ID y mapeados referencialmente?

 public class Command where T : ISerializable { T _target; int _actionId; int _conditionId; public Command(T Target, int ActionId, int ConditionId) { _target = Target; _actionId = ActionId; _conditionId = ConditionId; } public bool FireRule() { Func theCondition = conditionMap.LookupCondition(_conditionId) Action theAction = actionMap.LookupAction(_actionId); if (theCondition(_target)) { theAction(_target); return true; } return false; } } 

La idea de serializar un delegado es muy arriesgada. Ahora bien, una expresión puede tener sentido, pero incluso eso es difícil de express, aunque las muestras de LINQ dynamic van de alguna manera a permitir una forma de expresión basada en texto.

¿Qué es exactamente lo que quieres hacer con un delegado serializado? Realmente no creo que esta sea una buena idea …

Dado que este estado es local, eso genera problemas al intentar configurar un mapeo.

¿El estado local no presentaría exactamente los mismos problemas para la serialización?

Supongamos que el comstackdor y el marco permitieron que esto funcionara:

 Other o = FromSomeWhere(); Thing t = OtherPlace(); target.OnWhatever = () => t.DoFoo() + o.DoBar(); target.Save(); 

Supongo que tenia que ser serializado también. Los métodos no tienen el estado, las instancias sí.

Más tarde, deserializas el objective. ¿No obtienes copias nuevas de t y o? ¿Estas copias no estarán sincronizadas con cualquier cambio en el t original?

Además: ¿no podría llamarse su ejemplo manual de esta manera?

 Other o = FromSomeWhere(); Thing t = OtherPlace(); target.OnWhatever = new DoFooBar() {Other = o, Thing = t} .Run; target.Save(); 

No estoy al 100% en esto, pero creo que si quieres “guardar” un delegado o algún código en la base de datos que puede ser bastante dynamic, lo que tienes que hacer es crear una expresión, entonces puedes comstackr la expresión en un Func <...>.

Conceptos básicos del árbol de expresiones

Invocaciones tardías con árboles de expresión

Un mapa de funciones me impediría usar el estado local en la acción / condiciones. La única forma de evitar esto sería crear una clase por función que requiera estado adicional.

Esto es lo que el comstackdor de C # está haciendo automáticamente para mí con funciones anónimas. Mi problema es la serialización de estas clases de comstackdor.

  Other o = FromSomeWhere(); Thing t = OtherPlace(); target.OnWhatever = () => t.DoFoo() + o.DoBar(); target.Save();c 

Intentando serializar eso fallaría. Dado que este estado es local, eso genera problemas al intentar configurar un mapeo. En cambio, tendría que declarar algo como esto:

 [Serializable] abstract class Command { public abstract T Run(); } class DoFooBar : Command { public Other Other { get; set; } public Thing Thing { get; set; } public override int Run() { return Thing.DoFoo() + Other.DoBar(); } } 

y luego usarlo así:

  DoFooBar cmd = new DoFooBar(); cmd.Other = FromSomewhere(); cmd.Thing = OtherPlace(); target.OnWhatever = cmd.Run; target.Save(); 

Básicamente, lo que esto significa es hacer manualmente lo que el comstackdor de C # está haciendo automáticamente.