在 C# 中序列化匿名委托
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/321827/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Serializing anonymous delegates in C#
提问by Joseph Kingry
I am trying to determine what issues could be caused by using the following serialization surrogate to enable serialization of anonymous functions/delegate/lambdas.
我试图确定使用以下序列化代理来启用匿名函数/委托/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;
}
}
Listing 1adapted fromCounting Demo
清单 1改编自Counting Demo
The main issue I can think of that might be a problem is that the anonymous class is an internal compiler detail and it's structure is not guaranteed to remain constant between revisions to the .NET Framework. I'm fairly certain this is the case based on my research into the similar problem with iterators.
我能想到的主要问题可能是匿名类是一个内部编译器细节,它的结构不能保证在 .NET Framework 的修订之间保持不变。根据我对迭代器类似问题的研究,我相当肯定这是这种情况。
Background
背景
I am investigating the serialization of anonymous functions. I was expecting this not to work, but found it did for some cases. As long as the lambda did *not& force the compiler to generate an anonymous class everything works fine.
我正在调查匿名函数的序列化。我原以为这不起作用,但发现它在某些情况下起作用。只要 lambda 没有 * 不强制编译器生成匿名类,一切正常。
A SerializationException is thrown if the compiler requires a generated class to implement the anonymous function. This is because the compiler generated class is not marked as serializable.
如果编译器需要生成的类来实现匿名函数,则会抛出 SerializationException。这是因为编译器生成的类未标记为可序列化。
Example
例子
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.
}
}
Listing 2
清单 2
This is similar to the issue of trying to serialize iteratorsand I had found the following code in a previous search (see countingdemo) Using the code from Listing 1and an ISurrogateSelector I was able to successfully serialize and deserialize the second failing example.
这类似于尝试序列化迭代器的问题,我在之前的搜索中找到了以下代码(请参阅计数演示) 使用清单 1 中的代码和 ISurrogateSelector 我能够成功地序列化和反序列化第二个失败的示例。
Objective
客观的
I have a system that is exposed via a web service. The system has a complex but small state (many objects, not a lot of properties per object). The state is saved in the ASP.NET Cache, but is also serialized to a BLOB in SQL in case of cache expiration. Some objects need to execute arbitrary "events" upon reaching some condition. Hence they have properties accepting Action/Func objects. Contrived example:
我有一个通过 Web 服务公开的系统。系统有一个复杂但很小的状态(很多对象,每个对象没有很多属性)。状态保存在 ASP.NET 缓存中,但也会在缓存过期的情况下序列化为 SQL 中的 BLOB。某些对象在达到某个条件时需要执行任意“事件”。因此,它们具有接受 Action/Func 对象的属性。人为的例子:
class Command
{
public Command(Action action, Func<bool> condition);
}
Somewhere else
别的地方
void DoSomethingWithThing(Thing thing)
{
state = Store.GetCurrentState();
Command cmd = new Command(() => thing.Foo(), () => thing.IsReady())
state.Add(cmd);
Store.Save(state);
}
采纳答案by Amy B
Did you see this post that I wrote as a followup to the CountingDemo: http://dotnet.agilekiwi.com/blog/2007/12/update-on-persistent-iterators.html? Unfortunately, Microsoft have confirmed that they probably will change the compiler details (one day), in a way that is likely to cause problems. (e.g. f/when you update to the new compiler, you won't be able to deserialise the stuff you saved under the old (current) compiler.)
您是否看到我在 CountingDemo 的后续文章中写的这篇文章:http: //dotnet.agilekiwi.com/blog/2007/12/update-on-persistent-iterators.html?不幸的是,微软已经确认他们可能会更改编译器详细信息(有一天),以一种可能会导致问题的方式。(例如,当您更新到新编译器时,您将无法反序列化您在旧(当前)编译器下保存的内容。)
回答by Marc Gravell
The whole idea of serializing a delegate is very risky. Now, an expressionmight make sense, but even that is hard to express - although the dynamic-LINQ samples go some way to allowing a form of text-based expression.
序列化委托的整个想法是非常冒险的。现在,表达式可能有意义,但即便如此也难以表达——尽管动态 LINQ 示例在某种程度上允许基于文本的表达式形式。
What exactly is it you want to dowith a serialized delegate? I really don't think this is a good idea...
你到底想用序列化委托做什么?我真的不认为这是一个好主意......
回答by Amy B
Some objects need execute arbitrary "events" reaching some condition.
一些对象需要执行任意的“事件”来达到某种条件。
Just how arbitrary are these events? Can they be counted, assigned ID's and mapped to referentially?
这些事件有多随意?可以对它们进行计数、分配 ID 并以引用方式映射到它们吗?
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;
}
}
回答by Joseph Kingry
A function map would prevent me from using local state in the action/conditions. The only way around this would work would be to create a class per function that required additional state.
函数映射会阻止我在操作/条件中使用本地状态。解决这个问题的唯一方法是为每个需要额外状态的函数创建一个类。
This is what the C# compiler is doing automatically for me with anonymous functions. My issue is the serialization of these compiler classes.
这就是 C# 编译器使用匿名函数自动为我做的事情。我的问题是这些编译器类的序列化。
Other o = FromSomeWhere();
Thing t = OtherPlace();
target.OnWhatever = () => t.DoFoo() + o.DoBar();
target.Save();c
Trying to serialize that would fail. Since this state is local though that leads to issues when trying to setup a mapping. Instead I'd have to declare something like this:
尝试序列化会失败。由于此状态是本地状态,因此在尝试设置映射时会导致问题。相反,我必须声明如下内容:
[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();
}
}
and then use it like this:
然后像这样使用它:
DoFooBar cmd = new DoFooBar();
cmd.Other = FromSomewhere();
cmd.Thing = OtherPlace();
target.OnWhatever = cmd.Run;
target.Save();
Essentially what this means is doing manually what the C# compiler is doing for me automatically.
本质上,这意味着手动执行 C# 编译器自动为我执行的操作。
回答by Amy B
Since this state is local though that leads to issues when trying to setup a mapping.
由于此状态是本地状态,因此在尝试设置映射时会导致问题。
Wouldn't local state present the exact same problems for serialization?
本地状态不会为序列化带来完全相同的问题吗?
Suppose the compiler and the framework allowed this to work:
假设编译器和框架允许它工作:
Other o = FromSomeWhere();
Thing t = OtherPlace();
target.OnWhatever = () => t.DoFoo() + o.DoBar();
target.Save();
I guess t and o had to be serialized too. The methods don't have the state, the instances do.
我猜 t 和 o 也必须被序列化。方法没有状态,实例有。
Later, you deserialize target. Don't you get new copies of t and o? Won't these copies be out of sync with any changes to the original t and o?
稍后,您反序列化目标。你没有得到 t 和 o 的新副本吗?这些副本不会与原始 t 和 o 的任何更改不同步吗?
Also: couldn't your manual example be called this way?
另外:不能以这种方式调用您的手动示例吗?
Other o = FromSomeWhere();
Thing t = OtherPlace();
target.OnWhatever = new DoFooBar() {Other = o, Thing = t} .Run;
target.Save();
回答by viggity
I'm not 100% on this, but I believe that if you want to "save" a delegate or some code to the database that can be fairly dynamic, what you need to do is create an Expression, then you can compile the expression into a Func<...>.
我不是 100% 对此,但我相信如果您想将委托或一些代码“保存”到可以相当动态的数据库中,您需要做的是创建一个表达式,然后您可以编译该表达式变成一个 Func<...>。