remarks - Serializar delegados anónimos en C#
summary example c# (6)
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 compilador 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 compilador a generar una clase anónima, todo funciona bien.
Se lanza una SerializationException si el compilador requiere una clase generada para implementar la función anónima. Esto se debe a que la clase generada por el compilador 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<int> succeeds = () => 5;
f.Serialize(m, succeeds);
// Example 2
Other o = new Other();
Func<int> 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<bool> 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);
}
Algunos objetos necesitan ejecutar "eventos" arbitrarios que alcancen alguna condición.
¿Qué tan arbitrarios son estos eventos? ¿Pueden ser contados, asignados ID y mapeados referencialmente?
public class Command<T> where T : ISerializable
{
T _target;
int _actionId;
int _conditionId;
public Command<T>(T Target, int ActionId, int ConditionId)
{
_target = Target;
_actionId = ActionId;
_conditionId = ConditionId;
}
public bool FireRule()
{
Func<T, bool> theCondition = conditionMap.LookupCondition<T>(_conditionId)
Action<T> theAction = actionMap.LookupAction<T>(_actionId);
if (theCondition(_target))
{
theAction(_target);
return true;
}
return false;
}
}
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 compilador 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 objetivo. ¿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();
¿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 compilador (un día), de una manera que probablemente cause problemas. (Por ejemplo, f / cuando actualice al compilador nuevo, no podrá deserializar las cosas que guardó con el compilador anterior (actual)).
La idea de serializar un delegado es muy arriesgada. Ahora bien, una expresión puede tener sentido, pero incluso eso es difícil de expresar, aunque las muestras de LINQ dinámico 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 ...
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 dinámico, lo que tienes que hacer es crear una expresión, entonces puedes compilar la expresión en un Func <...>.
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 compilador de C # está haciendo automáticamente para mí con funciones anónimas. Mi problema es la serialización de estas clases de compilador.
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<T>
{
public abstract T Run();
}
class DoFooBar : Command<int>
{
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 compilador de C # está haciendo automáticamente.