简体   繁体   English

在C#中序列化匿名委托

[英]Serializing anonymous delegates in C#

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 1 adapted from Counting 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. 我可以想到的主要问题可能是一个问题是匿名类是内部编译器细节,并且它的结构不能保证在.NET Framework的修订版之间保持不变。 I'm fairly certain this is the case based on my research into the similar problem with iterators. 我很确定这是基于我对迭代器的类似问题的研究。

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. 如果编译器需要生成的类来实现匿名函数,则抛出SerializationException。 This is because the compiler generated class is not marked as serializable. 这是因为编译器生成的类未标记为可序列化。

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 iterators and I had found the following code in a previous search (see countingdemo ) Using the code from Listing 1 and an ISurrogateSelector I was able to successfully serialize and deserialize the second failing example. 这类似于尝试序列化迭代器的问题,我在之前的搜索中找到了以下代码(请参阅countingdemo )使用清单1中的代码和ISurrogateSelector,我能够成功序列化和反序列化第二个失败的示例。

Objective 目的

I have a system that is exposed via a web service. 我有一个通过Web服务公开的系统。 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. 状态保存在ASP.NET缓存中,但在缓存过期的情况下也会序列化为SQL中的BLOB。 Some objects need to execute arbitrary "events" upon reaching some condition. 某些对象在达到某些条件时需要执行任意“事件”。 Hence they have properties accepting Action/Func objects. 因此,它们具有接受Action / Func对象的属性。 Contrived example: 举例:

    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);
    }

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;
  }  
}

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 ? 您是否看到我作为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. 不幸的是,微软已经确认他们可能会以一种可能导致问题的方式更改编译器详细信息(一天)。 (eg f/when you update to the new compiler, you won't be able to deserialise the stuff you saved under the old (current) compiler.) (例如,当您更新到新编译器时,您将无法反序列化在旧(当前)编译器下保存的内容。)

The whole idea of serializing a delegate is very risky. 序列化代表的整个想法非常危险。 Now, an expression might 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 do with a serialized delegate? 你想用序列化代表究竟想做什么 I really don't think this is a good idea... 我真的不认为这是个好主意......

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. 我想t和o也必须序列化。 The methods don't have the state, the instances do. 实例没有状态。

Later, you deserialize target. 之后,您反序列化目标。 Don't you get new copies of t and o? 你不是得到t和o的新副本吗? Won't these copies be out of sync with any changes to the original t and 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();

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<...>. 我不是百分之百,但我相信如果你想将一个委托或一些代码“保存”到可以相当动态的数据库,你需要做的是创建一个Expression,然后你可以编译表达式进入Func <...>。

Expression Tree Basics 表达树基础知识

Late Bound Invocations with Expression Trees 具有表达式树的后期绑定调用

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. 这就是C#编译器使用匿名函数自动执行的操作。 My issue is the serialization of these compiler classes. 我的问题是这些编译器类的序列化。

        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#编译器自动为我做的事情。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM